Skip to content

Commit 80d9b02

Browse files
committed
🐛修复点歌
1 parent 37dfad9 commit 80d9b02

3 files changed

Lines changed: 141 additions & 188 deletions

File tree

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ require (
3333
github.com/fumiama/unibase2n v0.0.0-20240530074540-ec743fd5a6d6
3434
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
3535
github.com/google/uuid v1.6.0
36+
github.com/guohuiyuan/music-lib v1.0.1
3637
github.com/jinzhu/gorm v1.9.16
3738
github.com/jozsefsallai/gophersauce v1.0.1
3839
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
117117
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
118118
github.com/gopxl/beep/v2 v2.1.1 h1:6FYIYMm2qPAdWkjX+7xwKrViS1x0Po5kDMdRkq8NVbU=
119119
github.com/gopxl/beep/v2 v2.1.1/go.mod h1:ZAm9TGQ9lvpoiFLd4zf5B1IuyxZhgRACMId1XJbaW0E=
120+
github.com/guohuiyuan/music-lib v1.0.1 h1:RE1/0o7cMD+zU4vWtau3P6Czl9fW1a1G3dbjbgTA5H0=
121+
github.com/guohuiyuan/music-lib v1.0.1/go.mod h1:0qJM2Ug7Ow7hmbX5M5jPBRnZ1UN2No9GX156LDAaJ80=
120122
github.com/jfreymuth/oggvorbis v1.0.5 h1:u+Ck+R0eLSRhgq8WTmffYnrVtSztJcYrl588DM4e3kQ=
121123
github.com/jfreymuth/oggvorbis v1.0.5/go.mod h1:1U4pqWmghcoVsCJJ4fRBKv9peUJMBHixthRlBeD6uII=
122124
github.com/jfreymuth/vorbis v1.0.2 h1:m1xH6+ZI4thH927pgKD8JOH4eaGRm18rEE9/0WKjvNE=

plugin/music/selecter.go

Lines changed: 138 additions & 188 deletions
Original file line numberDiff line numberDiff line change
@@ -1,239 +1,189 @@
1-
// Package music QQ音乐、网易云、酷狗、酷我、咪咕 点歌
1+
// Package music 整合多平台音乐点播能力(基于 music-lib 重构)
22
package music
33

44
import (
5-
"crypto/md5"
6-
"encoding/hex"
5+
"errors"
76
"fmt"
8-
"io"
9-
"net/http"
10-
"net/url"
11-
"strings"
12-
"time"
137

14-
"github.com/FloatTech/floatbox/web"
15-
16-
ctrl "github.com/FloatTech/zbpctrl"
17-
"github.com/FloatTech/zbputils/control"
8+
ctrl "github.com/FloatTech/zbpctrl" // 别名 zbpctrl 为 ctrl
9+
"github.com/FloatTech/zbputils/control" // 保持 control 原名
1810
"github.com/FloatTech/zbputils/ctxext"
19-
"github.com/tidwall/gjson"
11+
"github.com/guohuiyuan/music-lib/kugou"
12+
"github.com/guohuiyuan/music-lib/kuwo"
13+
"github.com/guohuiyuan/music-lib/migu"
14+
"github.com/guohuiyuan/music-lib/netease"
15+
"github.com/guohuiyuan/music-lib/qq"
2016
zero "github.com/wdvxdr1123/ZeroBot"
2117
"github.com/wdvxdr1123/ZeroBot/message"
2218
)
2319

24-
var (
25-
longZhuURL = "https://www.hhlqilongzhu.cn/api/joox/juhe_music.php?msg=%v"
26-
)
20+
// 平台映射:指令前缀 -> 平台名称
21+
var platformMap = map[string]string{
22+
"咪咕": "migu",
23+
"酷我": "kuwo",
24+
"酷狗": "kugou",
25+
"网易": "netease",
26+
"qq": "qq",
27+
"": "kuwo", // 默认点歌指向酷我
28+
}
2729

2830
func init() {
31+
// 注册指令处理器
2932
control.AutoRegister(&ctrl.Options[*zero.Ctx]{
3033
DisableOnDefault: false,
3134
Brief: "点歌",
32-
Help: "- 点歌[xxx]\n" +
35+
Help: "- 点歌[xxx] (默认酷我)\n" +
3336
"- 网易点歌[xxx]\n" +
3437
"- 酷我点歌[xxx]\n" +
3538
"- 酷狗点歌[xxx]\n" +
3639
"- 咪咕点歌[xxx]\n" +
3740
"- qq点歌[xxx]\n",
3841
}).OnRegex(`^(.{0,2})点歌\s?(.{1,25})$`).SetBlock(true).Limit(ctxext.LimitByUser).
3942
Handle(func(ctx *zero.Ctx) {
40-
// switch 平台
41-
switch ctx.State["regex_matched"].([]string)[1] {
42-
case "咪咕":
43-
ctx.SendChain(migu(ctx.State["regex_matched"].([]string)[2]))
44-
case "酷我":
45-
ctx.SendChain(kuwo(ctx.State["regex_matched"].([]string)[2]))
46-
case "酷狗":
47-
ctx.SendChain(kugou(ctx.State["regex_matched"].([]string)[2]))
48-
case "网易":
49-
ctx.SendChain(cloud163(ctx.State["regex_matched"].([]string)[2]))
50-
case "qq":
51-
ctx.SendChain(qqmusic(ctx.State["regex_matched"].([]string)[2]))
52-
default: // 默认聚合点歌
53-
ctx.SendChain(longzhu(ctx.State["regex_matched"].([]string)[2]))
43+
matches := ctx.State["regex_matched"].([]string)
44+
platformPrefix := matches[1]
45+
keyword := matches[2]
46+
47+
// 获取目标平台
48+
targetPlatform, ok := platformMap[platformPrefix]
49+
if !ok {
50+
ctx.SendChain(message.Text("不支持的点播平台:", platformPrefix))
51+
return
52+
}
53+
54+
// 执行点播并返回结果
55+
seg, err := getMusicSegment(targetPlatform, keyword)
56+
if err != nil {
57+
ctx.SendChain(message.Text("点歌失败:", err.Error()))
58+
return
5459
}
60+
ctx.SendChain(seg)
5561
})
5662
}
5763

58-
// longzhu 聚合平台
59-
func longzhu(keyword string) message.Segment {
60-
data, _ := web.GetData(fmt.Sprintf(longZhuURL, url.QueryEscape(keyword)))
61-
// 假设 data 是包含整个 JSON 数组的字节切片
62-
results := gjson.ParseBytes(data).Array()
63-
for _, result := range results {
64-
if strings.Contains(strings.ToLower(result.Get("title").String()), strings.ToLower(keyword)) {
65-
if musicURL := result.Get("full_track").String(); musicURL != "" {
66-
return message.Record(musicURL)
67-
}
68-
}
64+
// getMusicSegment 根据平台和关键词获取音乐消息段
65+
func getMusicSegment(platform, keyword string) (message.Segment, error) {
66+
switch platform {
67+
case "migu":
68+
return getMiguMusic(keyword)
69+
case "kuwo":
70+
return getKuwoMusic(keyword)
71+
case "kugou":
72+
return getKugouMusic(keyword)
73+
case "netease":
74+
return getNeteaseMusic(keyword)
75+
case "qq":
76+
return getQQMusic(keyword)
77+
default:
78+
return message.Segment{}, errors.New("未知的音乐平台:" + platform)
6979
}
80+
}
7081

71-
results = gjson.GetBytes(data, "#.full_track").Array()
72-
if len(results) > 0 {
73-
if musicURL := results[0].String(); musicURL != "" {
74-
return message.Record(musicURL)
75-
}
76-
}
82+
// --- 各平台适配层(基于 music-lib 实现) ---
7783

78-
return message.Text("点歌失败, 找不到 ", keyword, " 的相关结果")
79-
}
84+
// getMiguMusic 咪咕音乐点播
85+
func getMiguMusic(keyword string) (message.Segment, error) {
86+
songs, err := migu.Search(keyword)
87+
if err != nil || len(songs) == 0 {
88+
return message.Segment{}, errors.New("咪咕音乐未找到相关歌曲:" + keyword)
89+
}
90+
song := songs[0]
8091

81-
// migu 返回咪咕音乐卡片
82-
func migu(keyword string) message.Segment {
83-
headers := http.Header{
84-
"Cookie": []string{"audioplayer_exist=1; audioplayer_open=0; migu_cn_cookie_id=3ad476db-f021-4bda-ab91-c485ac3d56a0; Hm_lvt_ec5a5474d9d871cb3d82b846d861979d=1671119573; Hm_lpvt_ec5a5474d9d871cb3d82b846d861979d=1671119573; WT_FPC=id=279ef92eaf314cbb8d01671116477485:lv=1671119583092:ss=1671116477485"},
85-
"csrf": []string{"LWKACV45JSQ"},
86-
"User-Agent": []string{"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"},
87-
"Referer": []string{"http://m.music.migu.cn"},
88-
"proxy": []string{"false"},
92+
// 传入 &song (指针)
93+
playURL, err := migu.GetDownloadURL(&song)
94+
if err != nil || playURL == "" {
95+
return message.Segment{}, errors.New("获取咪咕播放链接失败:" + err.Error())
8996
}
90-
// 搜索音乐信息 第一首歌
91-
search, _ := url.Parse("http://m.music.migu.cn/migu/remoting/scr_search_tag")
92-
search.RawQuery = url.Values{
93-
"keyword": []string{keyword},
94-
"type": []string{"2"},
95-
"pgc": []string{"1"},
96-
"rows": []string{"10"},
97-
}.Encode()
98-
info := gjson.ParseBytes(netGet(search.String(), headers)).Get("musics.0")
99-
// 返回音乐卡片
97+
10098
return message.CustomMusic(
101-
fmt.Sprintf("https://music.migu.cn/v3/music/song/%s", info.Get("copyrightId").String()),
102-
info.Get("mp3").String(),
103-
info.Get("songName").String(),
104-
).Add("content", info.Get("artist").Str).Add("image", info.Get("cover").Str).Add("subtype", "migu")
99+
fmt.Sprintf("https://music.migu.cn/v3/music/song/%s", song.ID),
100+
playURL,
101+
song.Name,
102+
).Add("content", song.Artist).Add("image", song.Cover).Add("subtype", "migu"), nil
105103
}
106104

107-
// kuwo 返回酷我音乐卡片
108-
func kuwo(keyword string) message.Segment {
109-
headers := http.Header{
110-
"Cookie": []string{"Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1610284708,1610699237; _ga=GA1.2.1289529848.1591618534; kw_token=LWKACV45JSQ; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1610699468; _gid=GA1.2.1868980507.1610699238; _gat=1"},
111-
"csrf": []string{"LWKACV45JSQ"},
112-
"User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"},
113-
"Referer": []string{"https://www.kuwo.cn/search/list?key="},
105+
// getKuwoMusic 酷我音乐点播
106+
func getKuwoMusic(keyword string) (message.Segment, error) {
107+
songs, err := kuwo.Search(keyword)
108+
if err != nil || len(songs) == 0 {
109+
return message.Segment{}, errors.New("酷我音乐未找到相关歌曲:" + keyword)
114110
}
115-
// 搜索音乐信息 第一首歌
116-
search, _ := url.Parse("https://www.kuwo.cn/api/www/search/searchMusicBykeyWord")
117-
search.RawQuery = url.Values{
118-
"key": []string{keyword},
119-
"pn": []string{"1"},
120-
"rn": []string{"1"},
121-
"httpsStatus": []string{"1"},
122-
}.Encode()
123-
info := gjson.ParseBytes(netGet(search.String(), headers)).Get("data.list.0")
124-
// 获得音乐直链
125-
music, _ := url.Parse("http://www.kuwo.cn/api/v1/www/music/playUrl")
126-
music.RawQuery = url.Values{
127-
"mid": []string{fmt.Sprintf("%d", info.Get("rid").Int())},
128-
"type": []string{"convert_url3"},
129-
"br": []string{"320kmp3"},
130-
"httpsStatus": []string{"1"},
131-
}.Encode()
132-
audio := gjson.ParseBytes(netGet(music.String(), headers))
133-
// 返回音乐卡片
111+
song := songs[0]
112+
113+
// 传入 &song (指针)
114+
playURL, err := kuwo.GetDownloadURL(&song)
115+
if err != nil || playURL == "" {
116+
return message.Segment{}, errors.New("获取酷我播放链接失败:" + err.Error())
117+
}
118+
134119
return message.CustomMusic(
135-
fmt.Sprintf("https://www.kuwo.cn/play_detail/%d", info.Get("rid").Int()),
136-
audio.Get("data.url").Str,
137-
info.Get("name").Str,
138-
).Add("content", info.Get("artist").Str).Add("image", info.Get("pic").Str).Add("subtype", "kuwo")
120+
fmt.Sprintf("https://www.kuwo.cn/play_detail/%s", song.ID),
121+
playURL,
122+
song.Name,
123+
).Add("content", song.Artist).Add("image", song.Cover).Add("subtype", "kuwo"), nil
139124
}
140125

141-
// kugou 返回酷狗音乐卡片
142-
func kugou(keyword string) message.Segment {
143-
stamp := time.Now().UnixNano() / 1e6
144-
hash := md5str(
145-
fmt.Sprintf(
146-
"NVPh5oo715z5DIWAeQlhMDsWXXQV4hwtbitrate=0callback=callback123clienttime=%dclientver=2000dfid=-inputtype=0iscorrection=1isfuzzy=0keyword=%smid=%dpage=1pagesize=30platform=WebFilterprivilege_filter=0srcappid=2919tag=emuserid=-1uuid=%dNVPh5oo715z5DIWAeQlhMDsWXXQV4hwt",
147-
stamp, keyword, stamp, stamp,
148-
),
149-
)
150-
// 搜索音乐信息 第一首歌
151-
h1 := http.Header{
152-
"User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"},
126+
// getKugouMusic 酷狗音乐点播
127+
func getKugouMusic(keyword string) (message.Segment, error) {
128+
songs, err := kugou.Search(keyword)
129+
if err != nil || len(songs) == 0 {
130+
return message.Segment{}, errors.New("酷狗音乐未找到相关歌曲:" + keyword)
153131
}
154-
search, _ := url.Parse("https://complexsearch.kugou.com/v2/search/song")
155-
search.RawQuery = url.Values{
156-
"callback": []string{"callback123"},
157-
"keyword": []string{keyword},
158-
"page": []string{"1"},
159-
"pagesize": []string{"30"},
160-
"bitrate": []string{"0"},
161-
"isfuzzy": []string{"0"},
162-
"tag": []string{"em"},
163-
"inputtype": []string{"0"},
164-
"platform": []string{"WebFilter"},
165-
"userid": []string{"-1"},
166-
"clientver": []string{"2000"},
167-
"iscorrection": []string{"1"},
168-
"privilege_filter": []string{"0"},
169-
"srcappid": []string{"2919"},
170-
"clienttime": []string{fmt.Sprintf("%d", stamp)},
171-
"mid": []string{fmt.Sprintf("%d", stamp)},
172-
"uuid": []string{fmt.Sprintf("%d", stamp)},
173-
"dfid": []string{"-"},
174-
"signature": []string{hash},
175-
}.Encode()
176-
res := netGet(search.String(), h1)
177-
info := gjson.ParseBytes(res[12 : len(res)-2]).Get("data.lists.0")
178-
// 获得音乐直链
179-
h2 := http.Header{
180-
"Cookie": []string{"kg_mid=d8e70a262c93d47599c6196c612d6f4f; Hm_lvt_aedee6983d4cfc62f509129360d6bb3d=1610278505,1611631363,1611722252; kg_dfid=33ZWee1kircl0jcJ1h0WF1fX; Hm_lpvt_aedee6983d4cfc62f509129360d6bb3d=1611727348; kg_dfid_collect=d41d8cd98f00b204e9800998ecf8427e"},
181-
"Host": []string{"wwwapi.kugou.com"},
182-
"TE": []string{"Trailers"},
183-
"User-Agent": []string{"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0"},
132+
song := songs[0]
133+
134+
// 传入 &song (指针)
135+
playURL, err := kugou.GetDownloadURL(&song)
136+
if err != nil || playURL == "" {
137+
return message.Segment{}, errors.New("获取酷狗播放链接失败:" + err.Error())
184138
}
185-
music := "https://wwwapi.kugou.com/yy/index.php?r=play%2Fgetdata&hash=" + info.Get("FileHash").Str + "&album_id=" + info.Get("AlbumID").Str
186-
audio := gjson.ParseBytes(netGet(music, h2)).Get("data")
187-
// 返回音乐卡片
139+
188140
return message.CustomMusic(
189-
"https://www.kugou.com/song/#hash="+audio.Get("hash").Str+"&album_id="+audio.Get("album_id").Str,
190-
strings.ReplaceAll(audio.Get("play_backup_url").Str, "\\/", "/"),
191-
audio.Get("audio_name").Str,
192-
).Add("content", audio.Get("author_name").Str).Add("image", audio.Get("img").Str).Add("subtype", "kugou")
141+
"https://www.kugou.com/",
142+
playURL,
143+
song.Name,
144+
).Add("content", song.Artist).Add("image", song.Cover).Add("subtype", "kugou"), nil
193145
}
194146

195-
// cloud163 返回网易云音乐卡片
196-
func cloud163(keyword string) (msg message.Segment) {
197-
requestURL := "http://music.163.com/api/search/get/web?type=1&limit=1&s=" + url.QueryEscape(keyword)
198-
data, err := web.GetData(requestURL)
199-
if err != nil {
200-
msg = message.Text("ERROR: ", err)
201-
return
147+
// getNeteaseMusic 网易云音乐点播
148+
func getNeteaseMusic(keyword string) (message.Segment, error) {
149+
songs, err := netease.Search(keyword)
150+
if err != nil || len(songs) == 0 {
151+
return message.Segment{}, errors.New("网易云音乐未找到相关歌曲:" + keyword)
202152
}
203-
msg = message.Music("163", gjson.ParseBytes(data).Get("result.songs.0.id").Int())
204-
return
205-
}
153+
song := songs[0]
206154

207-
// qqmusic 返回QQ音乐卡片
208-
func qqmusic(keyword string) (msg message.Segment) {
209-
requestURL := "https://c.y.qq.com/splcloud/fcgi-bin/smartbox_new.fcg?platform=yqq.json&key=" + url.QueryEscape(keyword)
210-
data, err := web.RequestDataWith(web.NewDefaultClient(), requestURL, "GET", "", web.RandUA(), nil)
211-
if err != nil {
212-
msg = message.Text("ERROR: ", err)
213-
return
155+
// 获取播放直链
156+
playURL, err := netease.GetDownloadURL(&song)
157+
if err != nil || playURL == "" {
158+
return message.Segment{}, errors.New("获取网易云播放链接失败:" + err.Error())
214159
}
215-
msg = message.Music("qq", gjson.ParseBytes(data).Get("data.song.itemlist.0.id").Int())
216-
return
217-
}
218160

219-
// md5str 返回字符串 MD5
220-
func md5str(s string) string {
221-
h := md5.New()
222-
h.Write([]byte(s))
223-
result := strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
224-
return result
161+
// 构造 CustomMusic
162+
return message.CustomMusic(
163+
fmt.Sprintf("https://music.163.com/#/song?id=%s", song.ID),
164+
playURL,
165+
song.Name,
166+
).Add("content", song.Artist).Add("image", song.Cover).Add("subtype", "163"), nil
225167
}
226168

227-
// netGet 返回请求数据
228-
func netGet(url string, header http.Header) []byte {
229-
client := &http.Client{}
230-
request, _ := http.NewRequest("GET", url, nil)
231-
request.Header = header
232-
res, err := client.Do(request)
233-
if err != nil {
234-
return nil
169+
// getQQMusic QQ音乐点播
170+
func getQQMusic(keyword string) (message.Segment, error) {
171+
songs, err := qq.Search(keyword)
172+
if err != nil || len(songs) == 0 {
173+
return message.Segment{}, errors.New("QQ音乐未找到相关歌曲:" + keyword)
174+
}
175+
song := songs[0]
176+
177+
// 获取播放直链
178+
playURL, err := qq.GetDownloadURL(&song)
179+
if err != nil || playURL == "" {
180+
return message.Segment{}, errors.New("获取QQ音乐播放链接失败:" + err.Error())
235181
}
236-
defer res.Body.Close()
237-
result, _ := io.ReadAll(res.Body)
238-
return result
182+
183+
// 构造 CustomMusic
184+
return message.CustomMusic(
185+
fmt.Sprintf("https://y.qq.com/n/ryqq/songDetail/%s", song.ID),
186+
playURL,
187+
song.Name,
188+
).Add("content", song.Artist).Add("image", song.Cover).Add("subtype", "qq"), nil
239189
}

0 commit comments

Comments
 (0)