修复猜歌已知问题 (#340)

* Update main.go

* Update struct.go

* Update main.go

* Update main.go
This commit is contained in:
方柳煜 2022-07-24 12:22:05 +08:00 committed by GitHub
parent ceb3df513d
commit c30c9192f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 105 additions and 59 deletions

View File

@ -39,10 +39,11 @@ const (
) )
var ( var (
catlist = make(map[string]int64, 100) catlist = make(map[string]int64, 100)
filelist []string filelist []string
cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒) musictypelist = "mp3;MP3;wav;WAV;amr;AMR;3gp;3GP;3gpp;3GPP;acc;ACC"
cfg config cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
cfg config
) )
func init() { // 插件主体 func init() { // 插件主体
@ -138,6 +139,11 @@ func init() { // 插件主体
if !strings.HasSuffix(musicPath, "/") { if !strings.HasSuffix(musicPath, "/") {
musicPath += "/" musicPath += "/"
} }
err = os.MkdirAll(cfg.MusicPath, 0755)
if err != nil {
ctx.SendChain(message.Text("[生成文件夹错误]ERROR:", err))
return
}
cfg.MusicPath = musicPath cfg.MusicPath = musicPath
case "本地": case "本地":
choice, err := strconv.ParseBool(value) choice, err := strconv.ParseBool(value)
@ -193,7 +199,9 @@ func init() { // 插件主体
ctx.SendChain(message.Text("解析网易云二维码失败, ERROR:", err)) ctx.SendChain(message.Text("解析网易云二维码失败, ERROR:", err))
return return
} }
ctx.SendChain(message.Text("[请使用手机APP扫描二维码或者进入网页扫码登录]\n", qrInfo.Data.Qrurl), message.Image("base64://"+strings.ReplaceAll(qrInfo.Data.Qrimg, "data:image/png;base64,", "")), message.Text("二维码有效时间为6分钟")) ctx.SendChain(message.Text("[请使用手机APP扫描二维码或者进入网页扫码登录]\n", qrInfo.Data.Qrurl),
message.Image("base64://"+strings.ReplaceAll(qrInfo.Data.Qrimg, "data:image/png;base64,", "")),
message.Text("二维码有效时间为6分钟,登陆后请耐心等待结果获取cookie过程有些漫长。"))
i := 0 i := 0
for range time.NewTicker(10 * time.Second).C { for range time.NewTicker(10 * time.Second).C {
apiURL := "https://music.cyrilstudio.top/login/qr/check?key=" + url.QueryEscape(keyInfo.Data.Unikey) apiURL := "https://music.cyrilstudio.top/login/qr/check?key=" + url.QueryEscape(keyInfo.Data.Unikey)
@ -234,11 +242,13 @@ func init() { // 插件主体
} }
} }
}) })
engine.OnRegex(`^添加歌单\s?(\d*)(\s(.*))?$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup). engine.OnRegex(`^添加歌单\s?(\d+)(\s(.*))?$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
listID := ctx.State["regex_matched"].([]string)[1] listID := ctx.State["regex_matched"].([]string)[1]
listName := ctx.State["regex_matched"].([]string)[3] listName := ctx.State["regex_matched"].([]string)[3]
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listID + "&cookie=" + url.QueryEscape(cfg.Cookie) ctx.SendChain(message.Text("正在校验歌单信息,请稍等"))
// 是否存在该歌单
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listID + "&cookie=" + cfg.Cookie
referer := "https://music.cyrilstudio.top" referer := "https://music.cyrilstudio.top"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua) data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil { if err != nil {
@ -251,6 +261,21 @@ func init() { // 插件主体
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err)) ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err))
return return
} }
// 是否有权限访问歌单列表内容
apiURL = "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + cfg.Cookie
referer = "https://music.163.com/"
data, err = web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil {
ctx.SendChain(message.Text("无法获取歌单列表\n ERROR:", err))
return
}
var musiclist topMusicInfo
err = json.Unmarshal(data, &musiclist)
if err != nil {
ctx.SendChain(message.Text("你的cookie在API中无权访问该歌单\n该歌单有可能是用户私人歌单"))
return
}
// 获取列表名字
if listName == "" { if listName == "" {
listName = parsed.Playlist.Name listName = parsed.Playlist.Name
} }
@ -303,7 +328,7 @@ func init() { // 插件主体
// 获取网易云歌单列表 // 获取网易云歌单列表
if cfg.API { if cfg.API {
catlist = make(map[string]int64, 100) catlist = make(map[string]int64, 100)
msg = append(msg, "\n当前添加的API歌单含有以下\n") msg = append(msg, "当前添加的API歌单含有以下\n")
for i, listInfo := range cfg.Playlist { for i, listInfo := range cfg.Playlist {
catlist[listInfo.Name] = listInfo.ID catlist[listInfo.Name] = listInfo.ID
msg = append(msg, strconv.Itoa(i)+":"+listInfo.Name) msg = append(msg, strconv.Itoa(i)+":"+listInfo.Name)
@ -318,18 +343,23 @@ func init() { // 插件主体
if err == nil { if err == nil {
files, err := ioutil.ReadDir(cfg.MusicPath) files, err := ioutil.ReadDir(cfg.MusicPath)
if err == nil { if err == nil {
msg = append(msg, "\n当前本地歌单含有以下\n") if len(files) == 0 {
i := 0 ctx.SendChain(message.Text("缓存目录没有读取到任何歌单"))
for _, name := range files { filelist = nil
if !name.IsDir() { } else {
continue msg = append(msg, "\n当前本地歌单含有以下\n")
i := 0
for _, name := range files {
if !name.IsDir() {
continue
}
filelist[i] = strconv.Itoa(i) + ":" + name.Name()
msg = append(msg, filelist[i])
if i%3 == 2 {
msg = append(msg, "\n")
}
i++
} }
filelist[i] = strconv.Itoa(i) + ":" + name.Name()
msg = append(msg, filelist[i])
if i%3 == 2 {
msg = append(msg, "\n")
}
i++
} }
} else { } else {
ctx.SendChain(message.Text("[读取本地列表错误]ERROR:", err)) ctx.SendChain(message.Text("[读取本地列表错误]ERROR:", err))
@ -354,6 +384,10 @@ func init() { // 插件主体
engine.OnSuffix("歌单信息").SetBlock(true).Limit(ctxext.LimitByGroup). engine.OnSuffix("歌单信息").SetBlock(true).Limit(ctxext.LimitByGroup).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
list := ctx.State["args"].(string) list := ctx.State["args"].(string)
if list == "" {
ctx.SendChain(message.Text("请输入歌单ID或者API歌单名称\n歌单ID为(网页/分享)链接的“playlist”后面的第一串数字"))
return
}
var listIDStr string var listIDStr string
for listName, listID := range catlist { for listName, listID := range catlist {
if list == listName || list == strconv.FormatInt(listID, 10) { if list == listName || list == strconv.FormatInt(listID, 10) {
@ -369,7 +403,7 @@ func init() { // 插件主体
} }
listIDStr = list listIDStr = list
} }
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listIDStr + "&cookie=" + url.QueryEscape(cfg.Cookie) apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listIDStr + "&cookie=" + cfg.Cookie
referer := "https://music.cyrilstudio.top" referer := "https://music.cyrilstudio.top"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua) data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil { if err != nil {
@ -400,15 +434,11 @@ func init() { // 插件主体
mode := ctx.State["regex_matched"].([]string)[3] mode := ctx.State["regex_matched"].([]string)[3]
if mode == "" { if mode == "" {
mode = "动画榜" mode = "动画榜"
catlist[mode] = 3001835560
} }
_, ok := catlist[mode] _, ok := catlist[mode]
switch { // 如果本地和API不存在该歌单
// 如果API没有开本地也不存在这个歌单 if !strings.Contains(strings.Join(filelist, " "), mode) && !ok {
case !cfg.API && !strings.Contains(strings.Join(filelist, " "), mode):
ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称"))
return
// 如果本地没有开,网易云也不存在这个歌单
case !cfg.Local && !ok:
ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称")) ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称"))
return return
} }
@ -420,6 +450,29 @@ func init() { // 插件主体
ctx.SendChain(message.Text(err)) ctx.SendChain(message.Text(err))
return return
} }
// 解析歌曲信息
music := strings.Split(musicName, ".")
// 获取音乐后缀
musictype := music[len(music)-1]
if !strings.Contains(musictypelist, musictype) {
ctx.SendChain(message.Text("抽取到了本地歌曲:\n",
musicName, "\n该歌曲不是音乐后缀请联系bot主人修改"))
return
}
// 获取音乐信息
musicInfo := strings.Split(strings.ReplaceAll(musicName, "."+musictype, ""), " - ")
infoNum := len(musicInfo)
if infoNum == 1 {
ctx.SendChain(message.Text("抽取到了本地歌曲:\n",
musicName, "\n该歌曲命名不符合命名规则请联系bot主人修改"))
return
}
answerString := "歌名:" + musicInfo[0] + "\n歌手:" + musicInfo[1]
musicAlia := ""
if infoNum > 2 {
musicAlia = musicInfo[2]
answerString += "\n其他信息:\n" + strings.ReplaceAll(musicAlia, "&", "\n")
}
// 切割音频生成3个10秒的音频 // 切割音频生成3个10秒的音频
outputPath := cachePath + gid + "/" outputPath := cachePath + gid + "/"
err = cutMusic(musicName, pathOfMusic, outputPath) err = cutMusic(musicName, pathOfMusic, outputPath)
@ -427,15 +480,6 @@ func init() { // 插件主体
ctx.SendChain(message.Text(err)) ctx.SendChain(message.Text(err))
return return
} }
// 解析歌曲信息
musicInfo := strings.Split(musicName, " - ")
infoNum := len(musicInfo)
answerString := "歌名:" + musicInfo[0] + "\n歌手:" + musicInfo[1]
musicAlia := ""
if infoNum > 2 {
musicAlia = musicInfo[2]
answerString += "\n其他信息:\n" + strings.ReplaceAll(musicAlia, "&", "\n")
}
// 进行猜歌环节 // 进行猜歌环节
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav")) ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav"))
var next *zero.FutureEvent var next *zero.FutureEvent
@ -660,24 +704,26 @@ func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err er
func getLocalMusic(files []fs.FileInfo) (musicName string) { func getLocalMusic(files []fs.FileInfo) (musicName string) {
if len(files) > 1 { if len(files) > 1 {
musicName = strings.Replace(files[rand.Intn(len(files))].Name(), ".mp3", "", 1) musicName = files[rand.Intn(len(files))].Name()
} else { } else {
musicName = strings.Replace(files[0].Name(), ".mp3", "", 1) musicName = files[0].Name()
} }
return return
} }
// 下载网易云歌单音乐 // 下载网易云歌单音乐
func getListMusic(listID, pathOfMusic string) (musicName string, err error) { func getListMusic(listID, pathOfMusic string) (musicName string, err error) {
apiURL := "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + url.QueryEscape(cfg.Cookie) apiURL := "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + cfg.Cookie
referer := "https://music.cyrilstudio.top" referer := "https://music.163.com/"
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua) data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
if err != nil { if err != nil {
err = errors.Errorf("无法获取歌单列表\n ERROR: %s", err)
return return
} }
var parsed topMusicInfo var parsed topMusicInfo
err = json.Unmarshal(data, &parsed) err = json.Unmarshal(data, &parsed)
if err != nil { if err != nil {
err = errors.Errorf("无法读取歌单列表\n ERROR: %s", err)
return return
} }
listlen := len(parsed.Songs) listlen := len(parsed.Songs)
@ -714,11 +760,11 @@ func getListMusic(listID, pathOfMusic string) (musicName string, err error) {
return return
} }
if cource != "" { if cource != "" {
musicName = name + " - " + artistName + " - " + cource musicName = name + " - " + artistName + " - " + cource + ".mp3"
} else { } else {
musicName = name + " - " + artistName musicName = name + " - " + artistName + ".mp3"
} }
downMusic := pathOfMusic + musicName + ".mp3" downMusic := pathOfMusic + musicName
if file.IsNotExist(downMusic) { if file.IsNotExist(downMusic) {
data, err = web.GetData(musicURL) data, err = web.GetData(musicURL)
if err != nil { if err != nil {
@ -740,7 +786,7 @@ func cutMusic(musicName, pathOfMusic, outputPath string) (err error) {
return return
} }
var stderr bytes.Buffer var stderr bytes.Buffer
cmdArguments := []string{"-y", "-i", pathOfMusic + musicName + ".mp3", cmdArguments := []string{"-y", "-i", pathOfMusic + musicName,
"-ss", cuttime[0], "-t", "10", file.BOTPATH + "/" + outputPath + "0.wav", "-ss", cuttime[0], "-t", "10", file.BOTPATH + "/" + outputPath + "0.wav",
"-ss", cuttime[1], "-t", "10", file.BOTPATH + "/" + outputPath + "1.wav", "-ss", cuttime[1], "-t", "10", file.BOTPATH + "/" + outputPath + "1.wav",
"-ss", cuttime[2], "-t", "10", file.BOTPATH + "/" + outputPath + "2.wav", "-hide_banner"} "-ss", cuttime[2], "-t", "10", file.BOTPATH + "/" + outputPath + "2.wav", "-hide_banner"}

View File

@ -168,25 +168,25 @@ type topList struct {
} `json:"al"` } `json:"al"`
Dt int `json:"dt"` Dt int `json:"dt"`
H struct { H struct {
Br int `json:"br"` Br int `json:"br"`
Fid int `json:"fid"` Fid int `json:"fid"`
Size int `json:"size"` Size int `json:"size"`
Vd int `json:"vd"` Vd float64 `json:"vd"`
Sr int `json:"sr"` Sr int `json:"sr"`
} `json:"h"` } `json:"h"`
M struct { M struct {
Br int `json:"br"` Br int `json:"br"`
Fid int `json:"fid"` Fid int `json:"fid"`
Size int `json:"size"` Size int `json:"size"`
Vd int `json:"vd"` Vd float64 `json:"vd"`
Sr int `json:"sr"` Sr int `json:"sr"`
} `json:"m"` } `json:"m"`
L struct { L struct {
Br int `json:"br"` Br int `json:"br"`
Fid int `json:"fid"` Fid int `json:"fid"`
Size int `json:"size"` Size int `json:"size"`
Vd int `json:"vd"` Vd float64 `json:"vd"`
Sr int `json:"sr"` Sr int `json:"sr"`
} `json:"l"` } `json:"l"`
Sq interface{} `json:"sq"` Sq interface{} `json:"sq"`
Hr interface{} `json:"hr"` Hr interface{} `json:"hr"`