为猜歌插件添加config (#285)

*  猜歌添加config

* 📚 猜歌帮助信息更新

* 📚 猜歌帮助信息更新

*  猜歌错误信息继承

*  错误处理

*  错误处理ep2

* 🐛 猜歌插件bug修正和命名修改

*  猜歌插件不再使用gjson

* Update struct.go

* 修复-动漫2无法下载歌曲问题

* 🐛 Resolve Compilation error

* 🐛 Resolve Lint

* 🐛 Resolve Lint

Co-authored-by: 方柳煜 <101934327+fangliuyu@users.noreply.github.com>
This commit is contained in:
Fox_white 2022-07-03 09:25:14 +08:00 committed by GitHub
parent 15a5b347e8
commit 5b2810e6c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 373 additions and 143 deletions

View File

@ -578,7 +578,11 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 团队猜歌 - [x] 团队猜歌
- [x] 设置缓存歌库路径 [绝对路径] - [x] 设置猜歌缓存歌库路径 [绝对路径]
- [x] 设置猜歌本地 [true/false]
- [x] 设置猜歌Api [true/false]
- 注:默认歌库为网易云热歌榜 - 注:默认歌库为网易云热歌榜

View File

@ -3,9 +3,12 @@ package guessmusic
import ( import (
"bytes" "bytes"
"encoding/json"
"io/fs"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"net/http" "net/http"
"net/url"
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
@ -17,13 +20,10 @@ import (
"github.com/wdvxdr1123/ZeroBot/message" "github.com/wdvxdr1123/ZeroBot/message"
ctrl "github.com/FloatTech/zbpctrl" ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/binary" "github.com/FloatTech/zbputils/control"
control "github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext" "github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/file" "github.com/FloatTech/zbputils/file"
"github.com/FloatTech/zbputils/web" "github.com/FloatTech/zbputils/web"
"github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
"github.com/wdvxdr1123/ZeroBot/extension/single" "github.com/wdvxdr1123/ZeroBot/extension/single"
) )
@ -32,8 +32,12 @@ const (
) )
var ( var (
musicPath = file.BOTPATH + "/data/guessmusic/music/" // 绝对路径,歌库根目录,通过指令进行更改 cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒) cfg = config{ // 默认 config
MusicPath: file.BOTPATH + "/data/guessmusic/music/", // 绝对路径,歌库根目录,通过指令进行更改
Local: true, // 是否使用本地音乐库
API: true, // 是否使用 Api
}
) )
func init() { // 插件主体 func init() { // 插件主体
@ -42,7 +46,9 @@ func init() { // 插件主体
Help: "猜歌插件该插件依赖ffmpeg\n" + Help: "猜歌插件该插件依赖ffmpeg\n" +
"- 个人猜歌\n" + "- 个人猜歌\n" +
"- 团队猜歌\n" + "- 团队猜歌\n" +
"- 设置缓存歌库路径 [绝对路径]\n" + "- 设置猜歌缓存歌库路径 [绝对路径]\n" +
"- 设置猜歌本地 [true/false]\n" +
"- 设置猜歌Api [true/false]\n" +
"注:默认歌库为网易云热歌榜\n" + "注:默认歌库为网易云热歌榜\n" +
"1.可在后面添加“-动漫”进行动漫歌猜歌\n-这个只能猜歌名和歌手\n" + "1.可在后面添加“-动漫”进行动漫歌猜歌\n-这个只能猜歌名和歌手\n" +
"2.可在后面添加“-动漫2”进行动漫歌猜歌\n-这个可以猜番名,但歌手经常“未知”", "2.可在后面添加“-动漫2”进行动漫歌猜歌\n-这个可以猜番名,但歌手经常“未知”",
@ -62,15 +68,28 @@ func init() { // 插件主体
if err != nil { if err != nil {
panic(err) panic(err)
} }
cfgfile := engine.DataFolder() + "setpath.txt" cfgFile := engine.DataFolder() + "config.json"
if file.IsExist(cfgfile) { if file.IsExist(cfgFile) {
b, err := os.ReadFile(cfgfile) reader, err := os.Open(cfgFile)
if err == nil { if err == nil {
musicPath = binary.BytesToString(b) err = json.NewDecoder(reader).Decode(&cfg)
logrus.Infoln("[guessmusic] set dir to", musicPath) if err != nil {
panic(err)
}
} else {
panic(err)
}
err = reader.Close()
if err != nil {
panic(err)
}
} else {
err = saveConfig(cfgFile)
if err != nil {
panic(err)
} }
} }
engine.OnRegex(`^设置缓存歌库路径(.*)$`, func(ctx *zero.Ctx) bool { engine.OnRegex(`^设置猜歌(缓存歌库路径|本地|Api)\s*(.*)$`, func(ctx *zero.Ctx) bool {
if !zero.SuperUserPermission(ctx) { if !zero.SuperUserPermission(ctx) {
ctx.SendChain(message.Text("只有bot主人可以设置")) ctx.SendChain(message.Text("只有bot主人可以设置"))
return false return false
@ -78,15 +97,35 @@ func init() { // 插件主体
return true return true
}).SetBlock(true). }).SetBlock(true).
Handle(func(ctx *zero.Ctx) { Handle(func(ctx *zero.Ctx) {
musicPath = ctx.State["regex_matched"].([]string)[1] option := ctx.State["regex_matched"].([]string)[1]
if musicPath == "" { value := ctx.State["regex_matched"].([]string)[2]
ctx.SendChain(message.Text("请输入正确的路径!")) switch option {
case "缓存歌库路径":
if value == "" {
ctx.SendChain(message.Text("请输入正确的路径!"))
return
}
musicPath := strings.ReplaceAll(value, "\\", "/")
if !strings.HasSuffix(musicPath, "/") {
musicPath += "/"
}
cfg.MusicPath = musicPath
case "本地":
choice, err := strconv.ParseBool(value)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
cfg.Local = choice
case "Api":
choice, err := strconv.ParseBool(value)
if err != nil {
ctx.SendChain(message.Text("ERROR:", err))
return
}
cfg.API = choice
} }
musicPath = strings.ReplaceAll(musicPath, "\\", "/") err = saveConfig(cfgFile)
if !strings.HasSuffix(musicPath, "/") {
musicPath += "/"
}
err := os.WriteFile(cfgfile, binary.StringToBytes(musicPath), 0644)
if err == nil { if err == nil {
ctx.SendChain(message.Text("成功!")) ctx.SendChain(message.Text("成功!"))
} else { } else {
@ -103,29 +142,29 @@ func init() { // 插件主体
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲名称|歌手|提示|取消]”\n一共3段语音6次机会")) ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲名称|歌手|提示|取消]”\n一共3段语音6次机会"))
} }
// 随机抽歌 // 随机抽歌
musicname, pathofmusic, err := musiclottery(mode, musicPath) musicName, pathOfMusic, err := musicLottery(mode, cfg.MusicPath)
if err != nil { if err != nil {
ctx.SendChain(message.Text(err)) ctx.SendChain(message.Text(err))
return return
} }
// 切割音频生成3个10秒的音频 // 切割音频生成3个10秒的音频
outputPath := cachePath + gid + "/" outputPath := cachePath + gid + "/"
err = musiccut(musicname, pathofmusic, outputPath) err = cutMusic(musicName, pathOfMusic, outputPath)
if err != nil { if err != nil {
ctx.SendChain(message.Text(err)) ctx.SendChain(message.Text(err))
return return
} }
// 进行猜歌环节 // 进行猜歌环节
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav")) ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav"))
answerstring := strings.Split(musicname, " - ") answerString := strings.Split(musicName, " - ")
var next *zero.FutureEvent var next *zero.FutureEvent
if ctx.State["regex_matched"].([]string)[1] == "个人" { if ctx.State["regex_matched"].([]string)[1] == "个人" {
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), ctx.CheckSession()) next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), ctx.CheckSession())
} else { } else {
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), zero.CheckGroup(ctx.Event.GroupID)) next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), zero.CheckGroup(ctx.Event.GroupID))
} }
var countofmusic = 0 // 音频数量 var musicCount = 0 // 音频数量
var countofanswer = 0 // 问答次数 var answerCount = 0 // 问答次数
recv, cancel := next.Repeat() recv, cancel := next.Repeat()
defer cancel() defer cancel()
wait := time.NewTimer(40 * time.Second) wait := time.NewTimer(40 * time.Second)
@ -139,24 +178,24 @@ func init() { // 插件主体
msg := make(message.Message, 0, 3) msg := make(message.Message, 0, 3)
msg = append(msg, message.Reply(ctx.Event.MessageID)) msg = append(msg, message.Reply(ctx.Event.MessageID))
msg = append(msg, message.Text("猜歌超时,游戏结束\n答案是:", msg = append(msg, message.Text("猜歌超时,游戏结束\n答案是:",
"\n歌名:", answerstring[0], "\n歌名:", answerString[0],
"\n歌手:", answerstring[1])) "\n歌手:", answerString[1]))
if mode == "-动漫2" { if mode == "-动漫2" {
msg = append(msg, message.Text("\n歌曲出自:", answerstring[2])) msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
} }
ctx.Send(msg) ctx.Send(msg)
return return
case <-wait.C: case <-wait.C:
wait.Reset(40 * time.Second) wait.Reset(40 * time.Second)
countofmusic++ musicCount++
if countofmusic > 2 { if musicCount > 2 {
wait.Stop() wait.Stop()
continue continue
} }
ctx.SendChain( ctx.SendChain(
message.Text("好像有些难度呢,再听这段音频,要仔细听哦"), message.Text("好像有些难度呢,再听这段音频,要仔细听哦"),
) )
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(countofmusic) + ".wav")) ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
case c := <-recv: case c := <-recv:
wait.Reset(40 * time.Second) wait.Reset(40 * time.Second)
tick.Reset(105 * time.Second) tick.Reset(105 * time.Second)
@ -171,10 +210,10 @@ func init() { // 插件主体
msg := make(message.Message, 0, 3) msg := make(message.Message, 0, 3)
msg = append(msg, message.Reply(c.Event.MessageID)) msg = append(msg, message.Reply(c.Event.MessageID))
msg = append(msg, message.Text("游戏已取消,猜歌答案是", msg = append(msg, message.Text("游戏已取消,猜歌答案是",
"\n歌名:", answerstring[0], "\n歌名:", answerString[0],
"\n歌手:", answerstring[1])) "\n歌手:", answerString[1]))
if mode == "-动漫2" { if mode == "-动漫2" {
msg = append(msg, message.Text("\n歌曲出自:", answerstring[2])) msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
} }
ctx.Send(msg) ctx.Send(msg)
return return
@ -185,8 +224,8 @@ func init() { // 插件主体
), ),
) )
case answer == "提示": case answer == "提示":
countofmusic++ musicCount++
if countofmusic > 2 { if musicCount > 2 {
wait.Stop() wait.Stop()
ctx.Send( ctx.Send(
message.ReplyWithMessage(c.Event.MessageID, message.ReplyWithMessage(c.Event.MessageID,
@ -201,87 +240,87 @@ func init() { // 插件主体
message.Text("再听这段音频,要仔细听哦"), message.Text("再听这段音频,要仔细听哦"),
), ),
) )
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(countofmusic) + ".wav")) ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
case strings.Contains(answerstring[0], answer) || strings.EqualFold(answerstring[0], answer): case strings.Contains(answerString[0], answer) || strings.EqualFold(answerString[0], answer):
wait.Stop() wait.Stop()
tick.Stop() tick.Stop()
after.Stop() after.Stop()
msg := make(message.Message, 0, 3) msg := make(message.Message, 0, 3)
msg = append(msg, message.Reply(c.Event.MessageID)) msg = append(msg, message.Reply(c.Event.MessageID))
msg = append(msg, message.Text("太棒了,你猜对歌曲名了!答案是", msg = append(msg, message.Text("太棒了,你猜对歌曲名了!答案是",
"\n歌名:", answerstring[0], "\n歌名:", answerString[0],
"\n歌手:", answerstring[1])) "\n歌手:", answerString[1]))
if mode == "-动漫2" { if mode == "-动漫2" {
msg = append(msg, message.Text("\n歌曲出自:", answerstring[2])) msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
} }
ctx.Send(msg) ctx.Send(msg)
return return
case answerstring[1] == "未知" && answer == "未知": case answerString[1] == "未知" && answer == "未知":
ctx.Send( ctx.Send(
message.ReplyWithMessage(c.Event.MessageID, message.ReplyWithMessage(c.Event.MessageID,
message.Text("该模式禁止回答“未知”"), message.Text("该模式禁止回答“未知”"),
), ),
) )
case strings.Contains(answerstring[1], answer) || strings.EqualFold(answerstring[1], answer): case strings.Contains(answerString[1], answer) || strings.EqualFold(answerString[1], answer):
wait.Stop() wait.Stop()
tick.Stop() tick.Stop()
after.Stop() after.Stop()
msg := make(message.Message, 0, 3) msg := make(message.Message, 0, 3)
msg = append(msg, message.Reply(c.Event.MessageID)) msg = append(msg, message.Reply(c.Event.MessageID))
msg = append(msg, message.Text("太棒了,你猜对歌手名了!答案是", msg = append(msg, message.Text("太棒了,你猜对歌手名了!答案是",
"\n歌名:", answerstring[0], "\n歌名:", answerString[0],
"\n歌手:", answerstring[1])) "\n歌手:", answerString[1]))
if mode == "-动漫2" { if mode == "-动漫2" {
msg = append(msg, message.Text("\n歌曲出自:", answerstring[2])) msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
} }
ctx.Send(msg) ctx.Send(msg)
return return
default: default:
if mode == "-动漫2" && (strings.Contains(answerstring[2], answer) || strings.EqualFold(answerstring[2], answer)) { if mode == "-动漫2" && (strings.Contains(answerString[2], answer) || strings.EqualFold(answerString[2], answer)) {
wait.Stop() wait.Stop()
tick.Stop() tick.Stop()
after.Stop() after.Stop()
ctx.Send(message.ReplyWithMessage(c.Event.MessageID, ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
message.Text("太棒了,你猜对番剧名了!答案是:", message.Text("太棒了,你猜对番剧名了!答案是:",
"\n歌名:", answerstring[0], "\n歌名:", answerString[0],
"\n歌手:", answerstring[1], "\n歌手:", answerString[1],
"\n歌曲出自:", answerstring[2]), "\n歌曲出自:", answerString[2]),
)) ))
return return
} }
countofmusic++ musicCount++
switch { switch {
case countofmusic > 2 && countofanswer < 6: case musicCount > 2 && answerCount < 6:
wait.Stop() wait.Stop()
countofanswer++ answerCount++
ctx.Send( ctx.Send(
message.ReplyWithMessage(c.Event.MessageID, message.ReplyWithMessage(c.Event.MessageID,
message.Text("答案不对哦,加油啊~"), message.Text("答案不对哦,加油啊~"),
), ),
) )
case countofmusic > 2: case musicCount > 2:
wait.Stop() wait.Stop()
tick.Stop() tick.Stop()
after.Stop() after.Stop()
msg := make(message.Message, 0, 3) msg := make(message.Message, 0, 3)
msg = append(msg, message.Reply(c.Event.MessageID)) msg = append(msg, message.Reply(c.Event.MessageID))
msg = append(msg, message.Text("次数到了,你没能猜出来。\n答案是:", msg = append(msg, message.Text("次数到了,你没能猜出来。\n答案是:",
"\n歌名:", answerstring[0], "\n歌名:", answerString[0],
"\n歌手:", answerstring[1])) "\n歌手:", answerString[1]))
if mode == "-动漫2" { if mode == "-动漫2" {
msg = append(msg, message.Text("\n歌曲出自:", answerstring[2])) msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
} }
ctx.Send(msg) ctx.Send(msg)
return return
default: default:
wait.Reset(40 * time.Second) wait.Reset(40 * time.Second)
countofanswer++ answerCount++
ctx.Send( ctx.Send(
message.ReplyWithMessage(c.Event.MessageID, message.ReplyWithMessage(c.Event.MessageID,
message.Text("答案不对,再听这段音频,要仔细听哦"), message.Text("答案不对,再听这段音频,要仔细听哦"),
), ),
) )
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(countofmusic) + ".wav")) ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
} }
} }
} }
@ -289,99 +328,139 @@ func init() { // 插件主体
}) })
} }
func saveConfig(cfgFile string) (err error) {
if reader, err := os.OpenFile(cfgFile, os.O_CREATE, os.ModePerm); err == nil {
err = json.NewEncoder(reader).Encode(&cfg)
if err != nil {
return err
}
} else {
return err
}
return nil
}
// 随机抽取音乐 // 随机抽取音乐
func musiclottery(mode, musicPath string) (musicname, pathofmusic string, err error) { func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err error) {
switch mode { switch mode {
case "-动漫": case "-动漫":
pathofmusic = musicPath + "动漫/" pathOfMusic = musicPath + "动漫/"
case "-动漫2": case "-动漫2":
pathofmusic = musicPath + "动漫2/" pathOfMusic = musicPath + "动漫2/"
default: default:
pathofmusic = musicPath + "歌榜/" pathOfMusic = musicPath + "歌榜/"
} }
err = os.MkdirAll(pathofmusic, 0755) err = os.MkdirAll(pathOfMusic, 0755)
if err != nil { if err != nil {
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err) err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
return return
} }
files, err := ioutil.ReadDir(pathofmusic) files, err := ioutil.ReadDir(pathOfMusic)
if err != nil { if err != nil {
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err) err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
return return
} }
// 随机抽取音乐从本地或者线上
switch { if cfg.Local && cfg.API {
case len(files) == 0: switch {
// 如果没有任何本地就下载歌曲 case len(files) == 0:
switch mode { // 如果没有任何本地就下载歌曲
case "-动漫": musicName, err = getAPIMusic(mode, pathOfMusic)
musicname, err = getpaugramdata(pathofmusic) if err != nil {
case "-动漫2": err = errors.Errorf("[本地数据为0歌曲下载错误]ERROR:%s", err)
musicname, err = getanimedata(pathofmusic) return
}
case rand.Intn(2) == 0:
// [0,1)只会取到0rand不允许的
musicName = getLocalMusic(files)
default: default:
musicname, err = getuomgdata(pathofmusic) musicName, err = getAPIMusic(mode, pathOfMusic)
if err != nil {
// 如果下载失败就从本地抽一个歌曲
musicName = getLocalMusic(files)
err = nil
}
} }
if err != nil { return
err = errors.Errorf("[本地数据为0歌曲下载错误]ERROR:%s", err) }
if cfg.Local {
if len(files) == 0 {
err = errors.New("[本地数据为0未开启API数据]")
return return
} }
case rand.Intn(2) == 0: musicName = getLocalMusic(files)
// [0,1)只会取到0rand不允许的 return
if len(files) > 1 { }
musicname = strings.Replace(files[rand.Intn(len(files))].Name(), ".mp3", "", 1) if cfg.API {
} else { musicName, err = getAPIMusic(mode, pathOfMusic)
musicname = strings.Replace(files[0].Name(), ".mp3", "", 1)
}
default:
switch mode {
case "-动漫":
musicname, err = getpaugramdata(pathofmusic)
case "-动漫2":
musicname, err = getanimedata(pathofmusic)
default:
musicname, err = getuomgdata(pathofmusic)
}
if err != nil { if err != nil {
// 如果下载失败就从本地抽一个歌曲 err = errors.Errorf("[获取API失败未开启本地数据] ERROR:%s", err)
if len(files) > 1 { return
musicname = strings.Replace(files[rand.Intn(len(files))].Name(), ".mp3", "", 1)
} else {
musicname = strings.Replace(files[0].Name(), ".mp3", "", 1)
}
err = nil
} }
return
}
err = errors.New("[未开启API以及本地数据]")
return
}
func getAPIMusic(mode string, musicPath string) (musicName string, err error) {
switch mode {
case "-动漫":
musicName, err = getPaugramData(musicPath)
case "-动漫2":
musicName, err = getAnimeData(musicPath)
default:
musicName, err = getNetEaseData(musicPath)
}
return
}
func getLocalMusic(files []fs.FileInfo) (musicName string) {
if len(files) > 1 {
musicName = strings.Replace(files[rand.Intn(len(files))].Name(), ".mp3", "", 1)
} else {
musicName = strings.Replace(files[0].Name(), ".mp3", "", 1)
} }
return return
} }
// 下载保罗API的歌曲 // 下载保罗API的歌曲
func getpaugramdata(musicPath string) (musicname string, err error) { func getPaugramData(musicPath string) (musicName string, err error) {
api := "https://api.paugram.com/acgm/?list=1" api := "https://api.paugram.com/acgm/?list=1"
referer := "https://api.paugram.com/" referer := "https://api.paugram.com/"
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua) data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
if err != nil { if err != nil {
return return
} }
name := gjson.Get(binary.BytesToString(data), "title").String() var parsed paugramData
artistsname := gjson.Get(binary.BytesToString(data), "artist").String() err = json.Unmarshal(data, &parsed)
musicurl := gjson.Get(binary.BytesToString(data), "link").String() if err != nil {
if name == "" || artistsname == "" {
err = errors.Errorf("the music is missed")
return return
} }
musicname = name + " - " + artistsname name := parsed.Title
downmusic := musicPath + "/" + musicname + ".mp3" artistsName := parsed.Artist
response, err := http.Head(musicurl) musicURL := parsed.Link
if err != nil || response.StatusCode != 200 { if name == "" || artistsName == "" {
err = errors.Errorf("the music is missed") err = errors.New("无法获API取歌曲信息")
return return
} }
if file.IsNotExist(downmusic) { musicName = name + " - " + artistsName
data, err = web.GetData(musicurl + ".mp3") downMusic := musicPath + "/" + musicName + ".mp3"
response, err := http.Head(musicURL)
if err != nil {
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
return
}
if response.StatusCode != 200 {
err = errors.Errorf("下载音乐失败, Status Code: %d", response.StatusCode)
return
}
if file.IsNotExist(downMusic) {
data, err = web.GetData(musicURL)
if err != nil { if err != nil {
return return
} }
err = os.WriteFile(downmusic, data, 0666) err = os.WriteFile(downMusic, data, 0666)
if err != nil { if err != nil {
return return
} }
@ -390,34 +469,66 @@ func getpaugramdata(musicPath string) (musicname string, err error) {
} }
// 下载animeMusic API的歌曲 // 下载animeMusic API的歌曲
func getanimedata(musicPath string) (musicname string, err error) { func getAnimeData(musicPath string) (musicName string, err error) {
api := "https://anime-music.jijidown.com/api/v2/music" api := "https://anime-music.jijidown.com/api/v2/music"
referer := "https://anime-music.jijidown.com/" referer := "https://anime-music.jijidown.com/"
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua) data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
if err != nil { if err != nil {
return return
} }
name := gjson.Get(binary.BytesToString(data), "res").Get("title").String() var parsed animeData
artistsname := gjson.Get(binary.BytesToString(data), "res").Get("author").String() err = json.Unmarshal(data, &parsed)
acgname := gjson.Get(binary.BytesToString(data), "res").Get("anime_info").Get("title").String() if err != nil {
musicurl := gjson.Get(binary.BytesToString(data), "res").Get("play_url").String()
if name == "" || artistsname == "" {
err = errors.Errorf("the music is missed")
return return
} }
musicname = name + " - " + artistsname + " - " + acgname name := parsed.Res.Title
downmusic := musicPath + "/" + musicname + ".mp3" artistName := parsed.Res.Author
response, err := http.Head(musicurl) acgName := parsed.Res.AnimeInfo.Title
if err != nil || response.StatusCode != 200 { //musicURL := parsed.Res.PlayURL
err = errors.Errorf("the music is missed") if name == "" || artistName == "" {
err = errors.New("无法获API取歌曲信息")
return return
} }
if file.IsNotExist(downmusic) { requestURL := "https://autumnfish.cn/search?keywords=" + url.QueryEscape(name+" "+artistName) + "&limit=1"
data, err = web.GetData(musicurl + ".mp3") if artistName == "未知" {
requestURL = "https://autumnfish.cn/search?keywords=" + url.QueryEscape(acgName+" "+name) + "&limit=1"
}
data, err = web.GetData(requestURL)
if err != nil {
err = errors.Errorf("API歌曲查询失败, ERROR: %s", err)
return
}
var autumnfish autumnfishData
err = json.Unmarshal(data, &autumnfish)
if err != nil {
return
}
if autumnfish.Code != 200 {
err = errors.Errorf("下载音乐失败, Status Code: %d", autumnfish.Code)
return
}
musicID := strconv.Itoa(autumnfish.Result.Songs[0].ID)
if artistName == "未知" {
artistName = strings.ReplaceAll(autumnfish.Result.Songs[0].Artists[0].Name, " - ", "-")
}
musicName = name + " - " + artistName + " - " + acgName
downMusic := musicPath + "/" + musicName + ".mp3"
musicURL := "http://music.163.com/song/media/outer/url?id=" + musicID
response, err := http.Head(musicURL)
if err != nil {
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
return
}
if response.StatusCode != 200 {
err = errors.Errorf("下载音乐失败, Status Code: %d", response.StatusCode)
return
}
if file.IsNotExist(downMusic) {
data, err = web.GetData(musicURL)
if err != nil { if err != nil {
return return
} }
err = os.WriteFile(downmusic, data, 0666) err = os.WriteFile(downMusic, data, 0666)
if err != nil { if err != nil {
return return
} }
@ -426,25 +537,33 @@ func getanimedata(musicPath string) (musicname string, err error) {
} }
// 下载网易云热歌榜音乐 // 下载网易云热歌榜音乐
func getuomgdata(musicPath string) (musicname string, err error) { func getNetEaseData(musicPath string) (musicName string, err error) {
api := "https://api.uomg.com/api/rand.music?sort=%E7%83%AD%E6%AD%8C%E6%A6%9C&format=json" api := "https://api.uomg.com/api/rand.music?sort=%E7%83%AD%E6%AD%8C%E6%A6%9C&format=json"
referer := "https://api.uomg.com/api/rand.music" referer := "https://api.uomg.com/api/rand.music"
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua) data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
if err != nil { if err != nil {
return return
} }
musicdata := gjson.Get(binary.BytesToString(data), "data") var parsed netEaseData
name := musicdata.Get("name").String() err = json.Unmarshal(data, &parsed)
musicurl := musicdata.Get("url").String() if err != nil {
artistsname := musicdata.Get("artistsname").String() return
musicname = name + " - " + artistsname }
downmusic := musicPath + "/" + musicname + ".mp3" name := parsed.Data.Name
if file.IsNotExist(downmusic) { musicURL := parsed.Data.URL
data, err = web.GetData(musicurl + ".mp3") artistsName := parsed.Data.Artistsname
if name == "" || artistsName == "" {
err = errors.New("无法获API取歌曲信息")
return
}
musicName = name + " - " + artistsName
downMusic := musicPath + "/" + musicName + ".mp3"
if file.IsNotExist(downMusic) {
data, err = web.GetData(musicURL)
if err != nil { if err != nil {
return return
} }
err = os.WriteFile(downmusic, data, 0666) err = os.WriteFile(downMusic, data, 0666)
if err != nil { if err != nil {
return return
} }
@ -453,14 +572,14 @@ func getuomgdata(musicPath string) (musicname string, err error) {
} }
// 切割音乐成三个10s音频 // 切割音乐成三个10s音频
func musiccut(musicname, pathofmusic, outputPath string) (err error) { func cutMusic(musicName, pathOfMusic, outputPath string) (err error) {
err = os.MkdirAll(outputPath, 0755) err = os.MkdirAll(outputPath, 0755)
if err != nil { if err != nil {
err = errors.Errorf("[生成歌曲目录错误]ERROR:%s", err) err = errors.Errorf("[生成歌曲目录错误]ERROR:%s", err)
return return
} }
var stderr bytes.Buffer var stderr bytes.Buffer
cmdArguments := []string{"-y", "-i", pathofmusic + musicname + ".mp3", cmdArguments := []string{"-y", "-i", pathOfMusic + musicName + ".mp3",
"-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"}

107
plugin/guessmusic/struct.go Normal file
View File

@ -0,0 +1,107 @@
package guessmusic
type config struct {
MusicPath string `json:"musicPath"`
Local bool `json:"local"`
API bool `json:"api"`
}
type paugramData struct {
ID int `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Album string `json:"album"`
Cover string `json:"cover"`
Lyric string `json:"lyric"`
SubLyric string `json:"sub_lyric"`
Link string `json:"link"`
Cached bool `json:"cached"`
}
type animeData struct {
Msg string `json:"msg"`
Res struct {
ID string `json:"id"`
AnimeInfo struct {
Desc string `json:"desc"`
ID string `json:"id"`
Atime int `json:"atime"`
Logo string `json:"logo"`
Year int `json:"year"`
Bg string `json:"bg"`
Title string `json:"title"`
Month int `json:"month"`
} `json:"anime_info"`
PlayURL string `json:"play_url"`
Atime int `json:"atime"`
Title string `json:"title"`
Author string `json:"author"`
Type string `json:"type"`
Recommend bool `json:"recommend"`
} `json:"res"`
Code int `json:"code"`
}
type netEaseData struct {
Code int `json:"code"`
Data struct {
Name string `json:"name"`
URL string `json:"url"`
Picurl string `json:"picurl"`
Artistsname string `json:"artistsname"`
} `json:"data"`
}
type autumnfishData struct {
Result struct {
Songs []struct {
ID int `json:"id"`
Name string `json:"name"`
Artists []struct {
ID int `json:"id"`
Name string `json:"name"`
PicURL interface{} `json:"picUrl"`
Alias []interface{} `json:"alias"`
AlbumSize int `json:"albumSize"`
PicID int `json:"picId"`
Img1V1URL string `json:"img1v1Url"`
Img1V1 int `json:"img1v1"`
Trans interface{} `json:"trans"`
} `json:"artists"`
Album struct {
ID int `json:"id"`
Name string `json:"name"`
Artist struct {
ID int `json:"id"`
Name string `json:"name"`
PicURL interface{} `json:"picUrl"`
Alias []interface{} `json:"alias"`
AlbumSize int `json:"albumSize"`
PicID int `json:"picId"`
Img1V1URL string `json:"img1v1Url"`
Img1V1 int `json:"img1v1"`
Trans interface{} `json:"trans"`
} `json:"artist"`
PublishTime int64 `json:"publishTime"`
Size int `json:"size"`
CopyrightID int `json:"copyrightId"`
Status int `json:"status"`
PicID int64 `json:"picId"`
Mark int `json:"mark"`
} `json:"album"`
Duration int `json:"duration"`
CopyrightID int `json:"copyrightId"`
Status int `json:"status"`
Alias []interface{} `json:"alias"`
Rtype int `json:"rtype"`
Ftype int `json:"ftype"`
Mvid int `json:"mvid"`
Fee int `json:"fee"`
RURL interface{} `json:"rUrl"`
Mark int `json:"mark"`
} `json:"songs"`
HasMore bool `json:"hasMore"`
SongCount int `json:"songCount"`
} `json:"result"`
Code int `json:"code"`
}