mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-07 15:40:26 +00:00
Compare commits
14 Commits
v1.4.1
...
v1.5.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e7191536a | ||
|
|
16ff1975ff | ||
|
|
b32c86863e | ||
|
|
d9b361f765 | ||
|
|
5b2810e6c5 | ||
|
|
15a5b347e8 | ||
|
|
477ffa825b | ||
|
|
84363b1513 | ||
|
|
01bf6f049b | ||
|
|
a8d93de000 | ||
|
|
2da5de3743 | ||
|
|
bf6bac7be6 | ||
|
|
394d2e4a08 | ||
|
|
0dd447e270 |
50
README.md
50
README.md
@@ -566,6 +566,30 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] >github -p [xxx]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>猜歌</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic"`
|
||||
|
||||
- 猜歌插件(该插件依赖ffmpeg)
|
||||
|
||||
- [x] 个人猜歌
|
||||
|
||||
- [x] 团队猜歌
|
||||
|
||||
- [x] 设置猜歌缓存歌库路径 [绝对路径]
|
||||
|
||||
- [x] 设置猜歌本地 [true/false]
|
||||
|
||||
- [x] 设置猜歌Api [true/false]
|
||||
|
||||
- 注:默认歌库为网易云热歌榜
|
||||
|
||||
- 1.可在后面添加“-动漫”进行动漫歌猜歌(这个只能猜歌名和歌手)
|
||||
|
||||
- 2.可在后面添加“-动漫2”进行动漫歌猜歌(这个可以猜番名,但歌手经常“未知”)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>炉石</summary>
|
||||
@@ -650,9 +674,13 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 团队听音练习
|
||||
|
||||
- [x] *.mid (解析上传的mid文件)
|
||||
- [x] *.mid (midi 转 txt)
|
||||
|
||||
- [x] 注: 该插件需要安装timidity,安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh
|
||||
- [x] midi制作*.txt (txt 转 midi)
|
||||
|
||||
- [x] 设置音色40 (0~127)
|
||||
|
||||
- [x] 注: 该插件需要安装timidity,linux安装脚本可参考 https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh , windows安装脚本可参考 https://gitcode.net/anto_july/midi/-/raw/master/timidity.bat , windows需要管理员模式运行
|
||||
|
||||
- [x] 符号说明: C5是中央C,后面不写数字,默认接5,Cb6<1,b代表降调,#代表升调,6比5高八度,<1代表音长×2,<3代表音长×8,<-1代表音长×0.5,<-3代表音长×0.125,R是休止符
|
||||
|
||||
@@ -858,6 +886,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
- [x] 抽塔罗牌
|
||||
- [x] 抽n张塔罗牌
|
||||
- [x] 解塔罗牌[牌名]
|
||||
- [x] 塔罗牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]"
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -921,23 +950,6 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 更新gal
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>早报</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/zaobao"`
|
||||
|
||||
- api早上8点更新,推荐定时在8点30后。配合插件`job`中的记录在"cron"触发的指令使用
|
||||
|
||||
- [x] /启用 zaobao
|
||||
|
||||
- [x] /禁用 zaobao
|
||||
|
||||
```
|
||||
记录在"00 9 * * *"触发的指令
|
||||
今日早报
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>舔狗日记</summary>
|
||||
|
||||
1
go.mod
1
go.mod
@@ -28,7 +28,6 @@ require (
|
||||
github.com/tidwall/gjson v1.14.1
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee
|
||||
gitlab.com/gomidi/midi v1.23.7
|
||||
gitlab.com/gomidi/midi/v2 v2.0.17
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd
|
||||
)
|
||||
|
||||
2
go.sum
2
go.sum
@@ -259,8 +259,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
gitlab.com/gomidi/midi v1.23.7 h1:I6qKoIk9s9dcX+pNf0jC+tziCzJFn82bMpuntRkLeik=
|
||||
gitlab.com/gomidi/midi v1.23.7/go.mod h1:3ohtNOhqoSakkuLG/Li1OI6I3J1c2LErnJF5o/VBq1c=
|
||||
gitlab.com/gomidi/midi/v2 v2.0.17 h1:kf16wNwFFOskl0trvarOwMuZUQICdIGn37LP9QqIRuo=
|
||||
gitlab.com/gomidi/midi/v2 v2.0.17/go.mod h1:quTyMKSQ4Klevxu6gY4gy2USbeZra0fV5SalndmPfsY=
|
||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
var (
|
||||
info = [...]string{
|
||||
"* OneBot + ZeroBot + Golang",
|
||||
"* Version 1.4.1 - 2022-06-16 13:31:06 +0800 CST",
|
||||
"* Version 1.5.0-beta2 - 2022-07-03 18:24:34 +0800 CST",
|
||||
"* Copyright © 2020 - 2022 FloatTech. All Rights Reserved.",
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
|
||||
}
|
||||
|
||||
3
main.go
3
main.go
@@ -78,6 +78,7 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/genshin" // 原神抽卡
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/gif" // 制图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/github" // 搜索GitHub仓库
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic" // 猜歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hs" // 炉石
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/hyaku" // 百人一首
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/image_finder" // 关键字搜图
|
||||
@@ -113,10 +114,8 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count" // 聊天热词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wordle" // 猜单词
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ymgal" // 月幕galgame
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/zaobao" // 早报
|
||||
|
||||
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wtf" // 鬼东西
|
||||
// _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_push" // b站推送
|
||||
|
||||
// ^^^^ //
|
||||
// ^^^^^^^^^^^^^^ //
|
||||
|
||||
594
plugin/guessmusic/main.go
Normal file
594
plugin/guessmusic/main.go
Normal file
@@ -0,0 +1,594 @@
|
||||
// Package guessmusic 基于zbp的猜歌插件
|
||||
package guessmusic
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||
)
|
||||
|
||||
const (
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
|
||||
)
|
||||
|
||||
var (
|
||||
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() { // 插件主体
|
||||
engine := control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "猜歌插件(该插件依赖ffmpeg)\n" +
|
||||
"- 个人猜歌\n" +
|
||||
"- 团队猜歌\n" +
|
||||
"- 设置猜歌缓存歌库路径 [绝对路径]\n" +
|
||||
"- 设置猜歌本地 [true/false]\n" +
|
||||
"- 设置猜歌Api [true/false]\n" +
|
||||
"注:默认歌库为网易云热歌榜\n" +
|
||||
"1.可在后面添加“-动漫”进行动漫歌猜歌\n-这个只能猜歌名和歌手\n" +
|
||||
"2.可在后面添加“-动漫2”进行动漫歌猜歌\n-这个可以猜番名,但歌手经常“未知”",
|
||||
PrivateDataFolder: "guessmusic",
|
||||
}).ApplySingle(single.New(
|
||||
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
|
||||
single.WithPostFn[int64](func(ctx *zero.Ctx) {
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(ctx.Event.MessageID,
|
||||
message.Text("已经有正在进行的游戏..."),
|
||||
),
|
||||
)
|
||||
}),
|
||||
))
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
err := os.MkdirAll(cachePath, 0755)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cfgFile := engine.DataFolder() + "config.json"
|
||||
if file.IsExist(cfgFile) {
|
||||
reader, err := os.Open(cfgFile)
|
||||
if err == nil {
|
||||
err = json.NewDecoder(reader).Decode(&cfg)
|
||||
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(`^设置猜歌(缓存歌库路径|本地|Api)\s*(.*)$`, func(ctx *zero.Ctx) bool {
|
||||
if !zero.SuperUserPermission(ctx) {
|
||||
ctx.SendChain(message.Text("只有bot主人可以设置!"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
value := ctx.State["regex_matched"].([]string)[2]
|
||||
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
|
||||
}
|
||||
err = saveConfig(cfgFile)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^(个人|团队)猜歌(-动漫|-动漫2)?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
mode := ctx.State["regex_matched"].([]string)[2]
|
||||
gid := strconv.FormatInt(ctx.Event.GroupID, 10)
|
||||
if mode == "-动漫2" {
|
||||
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲名称|歌手|番剧|提示|取消]”\n一共3段语音,6次机会"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲名称|歌手|提示|取消]”\n一共3段语音,6次机会"))
|
||||
}
|
||||
// 随机抽歌
|
||||
musicName, pathOfMusic, err := musicLottery(mode, cfg.MusicPath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text(err))
|
||||
return
|
||||
}
|
||||
// 切割音频,生成3个10秒的音频
|
||||
outputPath := cachePath + gid + "/"
|
||||
err = cutMusic(musicName, pathOfMusic, outputPath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text(err))
|
||||
return
|
||||
}
|
||||
// 进行猜歌环节
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + "0.wav"))
|
||||
answerString := strings.Split(musicName, " - ")
|
||||
var next *zero.FutureEvent
|
||||
if ctx.State["regex_matched"].([]string)[1] == "个人" {
|
||||
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), ctx.CheckSession())
|
||||
} else {
|
||||
next = zero.NewFutureEvent("message", 999, false, zero.OnlyGroup, zero.RegexRule(`^-\S{1,}`), zero.CheckGroup(ctx.Event.GroupID))
|
||||
}
|
||||
var musicCount = 0 // 音频数量
|
||||
var answerCount = 0 // 问答次数
|
||||
recv, cancel := next.Repeat()
|
||||
defer cancel()
|
||||
wait := time.NewTimer(40 * time.Second)
|
||||
tick := time.NewTimer(105 * time.Second)
|
||||
after := time.NewTimer(120 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
ctx.SendChain(message.Text("猜歌游戏,你还有15s作答时间"))
|
||||
case <-after.C:
|
||||
msg := make(message.Message, 0, 3)
|
||||
msg = append(msg, message.Reply(ctx.Event.MessageID))
|
||||
msg = append(msg, message.Text("猜歌超时,游戏结束\n答案是:",
|
||||
"\n歌名:", answerString[0],
|
||||
"\n歌手:", answerString[1]))
|
||||
if mode == "-动漫2" {
|
||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
||||
}
|
||||
ctx.Send(msg)
|
||||
return
|
||||
case <-wait.C:
|
||||
wait.Reset(40 * time.Second)
|
||||
musicCount++
|
||||
if musicCount > 2 {
|
||||
wait.Stop()
|
||||
continue
|
||||
}
|
||||
ctx.SendChain(
|
||||
message.Text("好像有些难度呢,再听这段音频,要仔细听哦"),
|
||||
)
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
|
||||
case c := <-recv:
|
||||
wait.Reset(40 * time.Second)
|
||||
tick.Reset(105 * time.Second)
|
||||
after.Reset(120 * time.Second)
|
||||
answer := strings.Replace(c.Event.Message.String(), "-", "", 1)
|
||||
switch {
|
||||
case answer == "取消":
|
||||
if c.Event.UserID == ctx.Event.UserID {
|
||||
wait.Stop()
|
||||
tick.Stop()
|
||||
after.Stop()
|
||||
msg := make(message.Message, 0, 3)
|
||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
||||
msg = append(msg, message.Text("游戏已取消,猜歌答案是",
|
||||
"\n歌名:", answerString[0],
|
||||
"\n歌手:", answerString[1]))
|
||||
if mode == "-动漫2" {
|
||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
||||
}
|
||||
ctx.Send(msg)
|
||||
return
|
||||
}
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("你无权限取消"),
|
||||
),
|
||||
)
|
||||
case answer == "提示":
|
||||
musicCount++
|
||||
if musicCount > 2 {
|
||||
wait.Stop()
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("已经没有提示了哦"),
|
||||
),
|
||||
)
|
||||
continue
|
||||
}
|
||||
wait.Reset(40 * time.Second)
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("再听这段音频,要仔细听哦"),
|
||||
),
|
||||
)
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
|
||||
case strings.Contains(answerString[0], answer) || strings.EqualFold(answerString[0], answer):
|
||||
wait.Stop()
|
||||
tick.Stop()
|
||||
after.Stop()
|
||||
msg := make(message.Message, 0, 3)
|
||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
||||
msg = append(msg, message.Text("太棒了,你猜对歌曲名了!答案是",
|
||||
"\n歌名:", answerString[0],
|
||||
"\n歌手:", answerString[1]))
|
||||
if mode == "-动漫2" {
|
||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
||||
}
|
||||
ctx.Send(msg)
|
||||
return
|
||||
case answerString[1] == "未知" && answer == "未知":
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("该模式禁止回答“未知”"),
|
||||
),
|
||||
)
|
||||
case strings.Contains(answerString[1], answer) || strings.EqualFold(answerString[1], answer):
|
||||
wait.Stop()
|
||||
tick.Stop()
|
||||
after.Stop()
|
||||
msg := make(message.Message, 0, 3)
|
||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
||||
msg = append(msg, message.Text("太棒了,你猜对歌手名了!答案是",
|
||||
"\n歌名:", answerString[0],
|
||||
"\n歌手:", answerString[1]))
|
||||
if mode == "-动漫2" {
|
||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
||||
}
|
||||
ctx.Send(msg)
|
||||
return
|
||||
default:
|
||||
if mode == "-动漫2" && (strings.Contains(answerString[2], answer) || strings.EqualFold(answerString[2], answer)) {
|
||||
wait.Stop()
|
||||
tick.Stop()
|
||||
after.Stop()
|
||||
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("太棒了,你猜对番剧名了!答案是:",
|
||||
"\n歌名:", answerString[0],
|
||||
"\n歌手:", answerString[1],
|
||||
"\n歌曲出自:", answerString[2]),
|
||||
))
|
||||
return
|
||||
}
|
||||
musicCount++
|
||||
switch {
|
||||
case musicCount > 2 && answerCount < 6:
|
||||
wait.Stop()
|
||||
answerCount++
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("答案不对哦,加油啊~"),
|
||||
),
|
||||
)
|
||||
case musicCount > 2:
|
||||
wait.Stop()
|
||||
tick.Stop()
|
||||
after.Stop()
|
||||
msg := make(message.Message, 0, 3)
|
||||
msg = append(msg, message.Reply(c.Event.MessageID))
|
||||
msg = append(msg, message.Text("次数到了,你没能猜出来。\n答案是:",
|
||||
"\n歌名:", answerString[0],
|
||||
"\n歌手:", answerString[1]))
|
||||
if mode == "-动漫2" {
|
||||
msg = append(msg, message.Text("\n歌曲出自:", answerString[2]))
|
||||
}
|
||||
ctx.Send(msg)
|
||||
return
|
||||
default:
|
||||
wait.Reset(40 * time.Second)
|
||||
answerCount++
|
||||
ctx.Send(
|
||||
message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("答案不对,再听这段音频,要仔细听哦"),
|
||||
),
|
||||
)
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func saveConfig(cfgFile string) (err error) {
|
||||
if reader, err := os.Create(cfgFile); 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) {
|
||||
switch mode {
|
||||
case "-动漫":
|
||||
pathOfMusic = musicPath + "动漫/"
|
||||
case "-动漫2":
|
||||
pathOfMusic = musicPath + "动漫2/"
|
||||
default:
|
||||
pathOfMusic = musicPath + "歌榜/"
|
||||
}
|
||||
err = os.MkdirAll(pathOfMusic, 0755)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
files, err := ioutil.ReadDir(pathOfMusic)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
if cfg.Local && cfg.API {
|
||||
switch {
|
||||
case len(files) == 0:
|
||||
// 如果没有任何本地就下载歌曲
|
||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[本地数据为0,歌曲下载错误]ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
case rand.Intn(2) == 0:
|
||||
// [0,1)只会取到0,rand不允许的
|
||||
musicName = getLocalMusic(files)
|
||||
default:
|
||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
||||
if err != nil {
|
||||
// 如果下载失败就从本地抽一个歌曲
|
||||
musicName = getLocalMusic(files)
|
||||
err = nil
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if cfg.Local {
|
||||
if len(files) == 0 {
|
||||
err = errors.New("[本地数据为0,未开启API数据]")
|
||||
return
|
||||
}
|
||||
musicName = getLocalMusic(files)
|
||||
return
|
||||
}
|
||||
if cfg.API {
|
||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[获取API失败,未开启本地数据] ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// 下载保罗API的歌曲
|
||||
func getPaugramData(musicPath string) (musicName string, err error) {
|
||||
api := "https://api.paugram.com/acgm/?list=1"
|
||||
referer := "https://api.paugram.com/"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var parsed paugramData
|
||||
err = json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
name := parsed.Title
|
||||
artistsName := parsed.Artist
|
||||
musicURL := parsed.Link
|
||||
if name == "" || artistsName == "" {
|
||||
err = errors.New("无法获API取歌曲信息")
|
||||
return
|
||||
}
|
||||
musicName = name + " - " + artistsName
|
||||
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 {
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(downMusic, data, 0666)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 下载animeMusic API的歌曲
|
||||
func getAnimeData(musicPath string) (musicName string, err error) {
|
||||
api := "https://anime-music.jijidown.com/api/v2/music"
|
||||
referer := "https://anime-music.jijidown.com/"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var parsed animeData
|
||||
err = json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
name := parsed.Res.Title
|
||||
artistName := parsed.Res.Author
|
||||
acgName := parsed.Res.AnimeInfo.Title
|
||||
//musicURL := parsed.Res.PlayURL
|
||||
if name == "" || artistName == "" {
|
||||
err = errors.New("无法获API取歌曲信息")
|
||||
return
|
||||
}
|
||||
requestURL := "https://autumnfish.cn/search?keywords=" + url.QueryEscape(name+" "+artistName) + "&limit=1"
|
||||
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 {
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(downMusic, data, 0666)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 下载网易云热歌榜音乐
|
||||
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"
|
||||
referer := "https://api.uomg.com/api/rand.music"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", referer, ua)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var parsed netEaseData
|
||||
err = json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
name := parsed.Data.Name
|
||||
musicURL := parsed.Data.URL
|
||||
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 {
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(downMusic, data, 0666)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 切割音乐成三个10s音频
|
||||
func cutMusic(musicName, pathOfMusic, outputPath string) (err error) {
|
||||
err = os.MkdirAll(outputPath, 0755)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[生成歌曲目录错误]ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
var stderr bytes.Buffer
|
||||
cmdArguments := []string{"-y", "-i", pathOfMusic + musicName + ".mp3",
|
||||
"-ss", cuttime[0], "-t", "10", file.BOTPATH + "/" + outputPath + "0.wav",
|
||||
"-ss", cuttime[1], "-t", "10", file.BOTPATH + "/" + outputPath + "1.wav",
|
||||
"-ss", cuttime[2], "-t", "10", file.BOTPATH + "/" + outputPath + "2.wav", "-hide_banner"}
|
||||
cmd := exec.Command("ffmpeg", cmdArguments...)
|
||||
cmd.Stderr = &stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
err = errors.Errorf("[生成歌曲错误]ERROR:%s", stderr.String())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
107
plugin/guessmusic/struct.go
Normal file
107
plugin/guessmusic/struct.go
Normal 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"`
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"time"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
@@ -22,7 +23,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
"gitlab.com/gomidi/midi/gm"
|
||||
"gitlab.com/gomidi/midi/v2"
|
||||
"gitlab.com/gomidi/midi/v2/smf"
|
||||
)
|
||||
@@ -30,11 +30,13 @@ import (
|
||||
func init() {
|
||||
engine := control.Register("midicreate", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "midi音乐制作,该插件需要安装timidity,安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh\n" +
|
||||
Help: "midi音乐制作,该插件需要安装timidity,linux安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh,windows安装脚本可参考https://gitcode.net/anto_july/midi/-/raw/master/timidity.bat,windows需要管理员模式运行\n" +
|
||||
"- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n" +
|
||||
"- 个人听音练习\n" +
|
||||
"- 团队听音练习\n" +
|
||||
"- *.mid (解析上传的mid文件)",
|
||||
"- *.mid (midi 转 txt)\n" +
|
||||
"- midi制作*.txt (txt 转 midi)\n" +
|
||||
"- 设置音色40 (0~127)",
|
||||
PrivateDataFolder: "midicreate",
|
||||
})
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
@@ -48,7 +50,7 @@ func init() {
|
||||
uid := ctx.Event.UserID
|
||||
input := ctx.State["args"].(string)
|
||||
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||
cmidiFile, err := str2music(input, midiFile)
|
||||
cmidiFile, err := str2music(ctx, input, midiFile)
|
||||
if err != nil {
|
||||
if file.IsExist(midiFile) {
|
||||
ctx.UploadThisGroupFile(file.BOTPATH+"/"+midiFile, filepath.Base(midiFile), "")
|
||||
@@ -87,7 +89,7 @@ func init() {
|
||||
target := uint8(55 + rand.Intn(34))
|
||||
answer := name(target) + strconv.Itoa(int(target/12))
|
||||
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||
cmidiFile, err := str2music(answer, midiFile)
|
||||
cmidiFile, err := str2music(ctx, answer, midiFile)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:听音练习结束, 无法转换midi文件, ", err))
|
||||
return
|
||||
@@ -137,7 +139,7 @@ func init() {
|
||||
),
|
||||
)
|
||||
midiFile = cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||
cmidiFile, err = str2music(c.Event.Message.String(), midiFile)
|
||||
cmidiFile, err = str2music(ctx, c.Event.Message.String(), midiFile)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: can't convert midi file,", err))
|
||||
return
|
||||
@@ -172,7 +174,7 @@ func init() {
|
||||
target = uint8(55 + rand.Intn(34))
|
||||
answer = name(target) + strconv.Itoa(int(target/12))
|
||||
midiFile = cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||
cmidiFile, err = str2music(answer, midiFile)
|
||||
cmidiFile, err = str2music(ctx, answer, midiFile)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:听音练习结束, 无法转换midi文件, ", err))
|
||||
return
|
||||
@@ -193,7 +195,7 @@ func init() {
|
||||
)
|
||||
time.Sleep(time.Millisecond * 500)
|
||||
midiFile = cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||
cmidiFile, err = str2music(c.Event.Message.String(), midiFile)
|
||||
cmidiFile, err = str2music(ctx, c.Event.Message.String(), midiFile)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: can't convert midi file,", err))
|
||||
return
|
||||
@@ -231,8 +233,54 @@ func init() {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
midStr := mid2txt(data)
|
||||
ctx.SendChain(message.Text("文件名:", ctx.Event.File.Name, "\n转化的midi字符:", midStr))
|
||||
s, err := smf.ReadFrom(bytes.NewReader(data))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
for i := 0; i < int(s.NumTracks()); i++ {
|
||||
midStr := mid2txt(data, i)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
fileName := strings.ReplaceAll(cachePath+"/"+ctx.Event.File.Name, ".mid", fmt.Sprintf("-%d.txt", i))
|
||||
_ = os.WriteFile(fileName, binary.StringToBytes(midStr), 0666)
|
||||
ctx.UploadThisGroupFile(file.BOTPATH+"/"+fileName, filepath.Base(fileName), "")
|
||||
}
|
||||
})
|
||||
engine.On("notice/group_upload", func(ctx *zero.Ctx) bool {
|
||||
return path.Ext(ctx.Event.File.Name) == ".txt" && strings.Contains(ctx.Event.File.Name, "midi制作")
|
||||
}).SetBlock(false).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
fileURL := ctx.GetThisGroupFileUrl(ctx.Event.File.BusID, ctx.Event.File.ID)
|
||||
data, err := web.GetData(fileURL)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
uid := ctx.Event.UserID
|
||||
midiFile := cachePath + strconv.FormatInt(uid, 10) + time.Now().Format("20060102150405") + "_midicreate.mid"
|
||||
cmidiFile, err := str2music(ctx, binary.BytesToString(data), midiFile)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:无法转换midi文件,", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + cmidiFile))
|
||||
})
|
||||
engine.OnPrefix("设置音色").SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
param := ctx.State["args"].(string)
|
||||
timbre, err := strconv.Atoi(param)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
err = setTimbreMode(ctx, int64(timbre))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("成功"))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -253,8 +301,8 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func str2music(input, midiFile string) (cmidiFile string, err error) {
|
||||
err = mkMidi(midiFile, input)
|
||||
func str2music(ctx *zero.Ctx, input, midiFile string) (cmidiFile string, err error) {
|
||||
err = mkMidi(ctx, midiFile, input)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -264,7 +312,7 @@ func str2music(input, midiFile string) (cmidiFile string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func mkMidi(filePath, input string) error {
|
||||
func mkMidi(ctx *zero.Ctx, filePath, input string) error {
|
||||
if file.IsExist(filePath) {
|
||||
return nil
|
||||
}
|
||||
@@ -276,7 +324,8 @@ func mkMidi(filePath, input string) error {
|
||||
tr.Add(0, smf.MetaMeter(4, 4))
|
||||
tr.Add(0, smf.MetaTempo(72))
|
||||
tr.Add(0, smf.MetaInstrument("Violin"))
|
||||
tr.Add(0, midi.ProgramChange(0, gm.Instr_Violin.Value()))
|
||||
timbre := getTimbreMode(ctx)
|
||||
tr.Add(0, midi.ProgramChange(0, uint8(timbre)))
|
||||
|
||||
k := strings.ReplaceAll(input, " ", "")
|
||||
|
||||
@@ -410,46 +459,43 @@ func processOne(note string) uint8 {
|
||||
return o(base, level)
|
||||
}
|
||||
|
||||
func mid2txt(midBytes []byte) (midStr string) {
|
||||
func mid2txt(midBytes []byte, trackNo int) (midStr string) {
|
||||
var (
|
||||
absTicksStart float64
|
||||
absTicksEnd float64
|
||||
startNote byte
|
||||
endNote byte
|
||||
defaultMetric = 960.0
|
||||
defaultTrackNo = 0
|
||||
absTicksStart float64
|
||||
absTicksEnd float64
|
||||
startNote byte
|
||||
endNote byte
|
||||
defaultMetric = 960.0
|
||||
)
|
||||
_ = smf.ReadTracksFrom(bytes.NewReader(midBytes)).
|
||||
_ = smf.ReadTracksFrom(bytes.NewReader(midBytes), trackNo).
|
||||
Do(
|
||||
func(te smf.TrackEvent) {
|
||||
if !te.Message.IsMeta() && te.TrackNo == defaultTrackNo {
|
||||
if !te.Message.IsMeta() {
|
||||
b := te.Message.Bytes()
|
||||
if len(b) == 3 {
|
||||
if b[0] == 0x90 && b[2] > 0 {
|
||||
absTicksStart = float64(te.AbsTicks)
|
||||
startNote = b[1]
|
||||
}
|
||||
if b[0] == 0x80 || (b[0] == 0x90 && b[2] == 0x00) {
|
||||
absTicksEnd = float64(te.AbsTicks)
|
||||
endNote = b[1]
|
||||
if te.Message.Is(midi.NoteOnMsg) && b[2] > 0 {
|
||||
absTicksStart = float64(te.AbsTicks)
|
||||
startNote = b[1]
|
||||
}
|
||||
if te.Message.Is(midi.NoteOffMsg) || (te.Message.Is(midi.NoteOnMsg) && b[2] == 0x00) {
|
||||
absTicksEnd = float64(te.AbsTicks)
|
||||
endNote = b[1]
|
||||
if startNote == endNote {
|
||||
sign := name(b[1])
|
||||
level := b[1] / 12
|
||||
length := (absTicksEnd - absTicksStart) / defaultMetric
|
||||
midStr += sign
|
||||
if level != 5 {
|
||||
midStr += strconv.Itoa(int(level))
|
||||
}
|
||||
pow := int(math.Round(math.Log2(length)))
|
||||
if pow >= -4 && pow != 0 {
|
||||
midStr += "<" + strconv.Itoa(pow)
|
||||
}
|
||||
startNote = 0
|
||||
endNote = 0
|
||||
}
|
||||
}
|
||||
if (b[0] == 0x80 || (b[0] == 0x90 && b[2] == 0x00)) && startNote == endNote {
|
||||
sign := name(b[1])
|
||||
level := b[1] / 12
|
||||
length := (absTicksEnd - absTicksStart) / defaultMetric
|
||||
midStr += sign
|
||||
if level != 5 {
|
||||
midStr += strconv.Itoa(int(level))
|
||||
}
|
||||
pow := int(math.Round(math.Log2(length)))
|
||||
if pow >= -4 && pow != 0 {
|
||||
midStr += "<" + strconv.Itoa(pow)
|
||||
}
|
||||
startNote = 0
|
||||
endNote = 0
|
||||
}
|
||||
if (b[0] == 0x90 && b[2] > 0) && absTicksStart > absTicksEnd {
|
||||
if (te.Message.Is(midi.NoteOnMsg) && b[2] > 0) && absTicksStart > absTicksEnd {
|
||||
length := (absTicksStart - absTicksEnd) / defaultMetric
|
||||
pow := int(math.Round(math.Log2(length)))
|
||||
if pow == 0 {
|
||||
@@ -463,3 +509,31 @@ func mid2txt(midBytes []byte) (midStr string) {
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
func setTimbreMode(ctx *zero.Ctx, timbre int64) error {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if timbre < 0 || timbre > 127 {
|
||||
return errors.New("音色应该在0~127之间")
|
||||
}
|
||||
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
return errors.New("no such plugin")
|
||||
}
|
||||
return m.SetData(gid, timbre)
|
||||
}
|
||||
|
||||
func getTimbreMode(ctx *zero.Ctx) (index int64) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
m, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if ok {
|
||||
index := m.GetData(gid)
|
||||
return index
|
||||
}
|
||||
return 40
|
||||
}
|
||||
|
||||
@@ -168,6 +168,9 @@ func (sql *婚姻登记) 花名册(gid int64) (list [][4]string, number int, err
|
||||
var info userinfo
|
||||
list = make([][4]string, 0, number)
|
||||
err = sql.db.FindFor(gidstr, &info, "GROUP BY user", func() error {
|
||||
if info.Target == 0 {
|
||||
return nil
|
||||
}
|
||||
dbinfo := [4]string{
|
||||
info.Username,
|
||||
strconv.FormatInt(info.User, 10),
|
||||
@@ -177,6 +180,9 @@ func (sql *婚姻登记) 花名册(gid int64) (list [][4]string, number int, err
|
||||
list = append(list, dbinfo)
|
||||
return nil
|
||||
})
|
||||
if len(list) == 0 {
|
||||
number = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -397,16 +403,16 @@ func init() {
|
||||
uid := ctx.Event.UserID
|
||||
gid := ctx.Event.GroupID
|
||||
if uid == fiancee { // 如果是自己
|
||||
switch rand.Intn(2) { // 二分之一概率浪费技能
|
||||
case 0:
|
||||
ctx.SendChain(message.Text("今日获得成就:自恋狂"))
|
||||
default:
|
||||
switch rand.Intn(3) {
|
||||
case 1:
|
||||
err := 民政局.登记(gid, uid, 0, "", "")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("今日获得成就:单身贵族"))
|
||||
default:
|
||||
ctx.SendChain(message.Text("今日获得成就:自恋狂"))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -569,28 +575,12 @@ func init() {
|
||||
ctx.SendChain(message.ImageBytes(data))
|
||||
cl()
|
||||
})
|
||||
engine.OnFullMatchGroup([]string{"闹离婚", "办离婚"}, zero.OnlyGroup, getdb).SetBlock(true).Limit(cdcheck, iscding2).
|
||||
engine.OnFullMatchGroup([]string{"闹离婚", "办离婚"}, zero.OnlyGroup, getdb, checkfiancee).SetBlock(true).Limit(cdcheck, iscding2).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
updatetime, err := 民政局.checkupdate(gid)
|
||||
switch {
|
||||
case err != nil:
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return
|
||||
case time.Now().Format("2006/01/02") != updatetime:
|
||||
if err := 民政局.重置(strconv.FormatInt(gid, 10)); err != nil {
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("今天你还没有结婚哦"))
|
||||
return
|
||||
}
|
||||
// 获取用户信息
|
||||
uid := ctx.Event.UserID
|
||||
info, uidstatus, err := 民政局.查户口(gid, uid)
|
||||
switch uidstatus {
|
||||
case 3:
|
||||
return
|
||||
case 2:
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return
|
||||
@@ -618,7 +608,7 @@ func init() {
|
||||
ctx.SendChain(message.Text(sendtext[4][1]))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`重置(所有|本群|/d+)?花名册`, zero.SuperUserPermission, getdb).SetBlock(true).Limit(ctxext.LimitByUser).
|
||||
engine.OnRegex(`^重置(所有|本群|/d+)?花名册$`, zero.SuperUserPermission, getdb).SetBlock(true).Limit(ctxext.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
cmd := "ALL"
|
||||
switch ctx.State["regex_matched"].([]string)[1] {
|
||||
@@ -683,25 +673,26 @@ func checkdog(ctx *zero.Ctx) bool {
|
||||
// 获取用户信息
|
||||
uidtarget, uidstatus, err1 := 民政局.查户口(gid, uid)
|
||||
fianceeinfo, fianceestatus, err2 := 民政局.查户口(gid, fiancee)
|
||||
if uidstatus == 2 || fianceestatus == 2 {
|
||||
switch {
|
||||
case uidstatus == 2 || fianceestatus == 2:
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err1, "\n", err2))
|
||||
return false
|
||||
}
|
||||
if uidstatus == 3 && fianceestatus == 3 { // 必须是两个单身
|
||||
case uidstatus == 3 && fianceestatus == 3: // 必须是两个单身
|
||||
return true
|
||||
}
|
||||
switch {
|
||||
case uidtarget.Target == fiancee: // 如果本就是一块
|
||||
ctx.SendChain(message.Text("笨蛋~你们明明已经在一起了啊w"))
|
||||
return false
|
||||
case uidstatus != 3 && uidtarget.Target == 0: // 如果是单身贵族
|
||||
ctx.SendChain(message.Text("今天的你是单身贵族噢"))
|
||||
return false
|
||||
case uidstatus == 1: // 如果如为攻
|
||||
ctx.SendChain(message.Text("笨蛋~你家里还有个吃白饭的w"))
|
||||
return false
|
||||
case uidstatus == 0: // 如果为受
|
||||
ctx.SendChain(message.Text("该是0就是0,当0有什么不好"))
|
||||
return false
|
||||
case uidstatus != 3 && uidtarget.Target == 0: // 如果是单身贵族
|
||||
ctx.SendChain(message.Text("今天的你是单身贵族噢"))
|
||||
case fianceestatus != 3 && fianceeinfo.Target == 0:
|
||||
ctx.SendChain(message.Text("今天的ta是单身贵族噢"))
|
||||
return false
|
||||
case fianceestatus == 1: // 如果如为攻
|
||||
ctx.SendChain(message.Text("他有别的女人了,你该放下了"))
|
||||
@@ -709,9 +700,6 @@ func checkdog(ctx *zero.Ctx) bool {
|
||||
case fianceestatus == 0: // 如果为受
|
||||
ctx.SendChain(message.Text("这是一个纯爱的世界,拒绝NTR"))
|
||||
return false
|
||||
case fianceestatus != 3 && fianceeinfo.Target == 0:
|
||||
ctx.SendChain(message.Text("今天的ta是单身贵族噢"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -739,11 +727,8 @@ func checkcp(ctx *zero.Ctx) bool {
|
||||
ctx.SendChain(message.Text("额,你的对象好像不存在?"))
|
||||
return false
|
||||
}
|
||||
uid := ctx.Event.UserID
|
||||
if fiancee == uid {
|
||||
return true
|
||||
}
|
||||
// 检查用户是否登记过
|
||||
uid := ctx.Event.UserID
|
||||
userinfo, uidstatus, err := 民政局.查户口(gid, uid)
|
||||
switch {
|
||||
case uidstatus == 2:
|
||||
@@ -755,6 +740,8 @@ func checkcp(ctx *zero.Ctx) bool {
|
||||
case uidstatus != 3 && userinfo.Target == 0: // 如果是单身贵族
|
||||
ctx.SendChain(message.Text("今天的你是单身贵族哦"))
|
||||
return false
|
||||
case fiancee == uid: // 自我攻略
|
||||
return true
|
||||
case uidstatus == 1: // 如果如为攻
|
||||
ctx.SendChain(message.Text("打灭,不给纳小妾!"))
|
||||
return false
|
||||
@@ -776,6 +763,36 @@ func checkcp(ctx *zero.Ctx) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
func iscding2(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("打灭,禁止离婚"))
|
||||
|
||||
func checkfiancee(ctx *zero.Ctx) bool {
|
||||
gid := ctx.Event.GroupID
|
||||
updatetime, err := 民政局.checkupdate(gid)
|
||||
switch {
|
||||
case err != nil:
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return false
|
||||
case time.Now().Format("2006/01/02") != updatetime:
|
||||
if err := 民政局.重置(strconv.FormatInt(gid, 10)); err != nil {
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return false
|
||||
}
|
||||
ctx.SendChain(message.Text("今天你还没有结婚哦"))
|
||||
return false
|
||||
}
|
||||
// 获取用户信息
|
||||
uid := ctx.Event.UserID
|
||||
_, uidstatus, err := 民政局.查户口(gid, uid)
|
||||
switch uidstatus {
|
||||
case 2:
|
||||
ctx.SendChain(message.Text("数据库发生问题力,请联系bot管理员\n[error]", err))
|
||||
return false
|
||||
case 3: // 如果是单身
|
||||
ctx.SendChain(message.Text("今天你还没有结婚哦"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func iscding2(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("打灭,禁止离婚 (你的技能正在CD中)"))
|
||||
}
|
||||
|
||||
@@ -9,8 +9,10 @@ import (
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
@@ -27,12 +29,20 @@ type card struct {
|
||||
Name string `json:"name"`
|
||||
cardInfo `json:"info"`
|
||||
}
|
||||
|
||||
type formation struct {
|
||||
CardsNum int `json:"cards_num"`
|
||||
IsCut bool `json:"is_cut"`
|
||||
Represent [][]string `json:"represent"`
|
||||
}
|
||||
type cardSet = map[string]card
|
||||
|
||||
var cardMap = make(cardSet, 30)
|
||||
var infoMap = make(map[string]cardInfo, 30)
|
||||
var formationMap = make(map[string]formation, 10)
|
||||
|
||||
// var cardName = make([]string, 22)
|
||||
// var cardName = make([]string, 30)
|
||||
// var formationName = make([]string, 10)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("tarot", &ctrl.Options[*zero.Ctx]{
|
||||
@@ -40,26 +50,43 @@ func init() {
|
||||
Help: "塔罗牌\n" +
|
||||
"- 抽塔罗牌\n" +
|
||||
"- 抽n张塔罗牌\n" +
|
||||
"- 解塔罗牌[牌名]",
|
||||
"- 解塔罗牌[牌名]\n" +
|
||||
"- 塔罗牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]",
|
||||
PublicDataFolder: "Tarot",
|
||||
}).ApplySingle(ctxext.DefaultSingle)
|
||||
|
||||
engine.OnRegex(`^抽(\d{1,2}张)?塔罗牌$`, ctxext.DoOnceOnSuccess(
|
||||
func(ctx *zero.Ctx) bool {
|
||||
data, err := engine.GetLazyData("tarots.json", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
err = json.Unmarshal(data, &cardMap)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
logrus.Infof("[tarot]读取%d张塔罗牌", len(cardMap))
|
||||
return true
|
||||
},
|
||||
)).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
getTarot := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
data, err := engine.GetLazyData("tarots.json", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
err = json.Unmarshal(data, &cardMap)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
for _, card := range cardMap {
|
||||
infoMapKey := strings.Split(card.Name, "(")[0]
|
||||
infoMap[infoMapKey] = card.cardInfo
|
||||
// 可以拿来显示大阿尔卡纳列表
|
||||
// cardName = append(cardName, infoMapKey)
|
||||
}
|
||||
logrus.Infof("[tarot]读取%d张大阿尔卡纳塔罗牌", len(cardMap))
|
||||
formation, err := engine.GetLazyData("formation.json", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
err = json.Unmarshal(formation, &formationMap)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
logrus.Infof("[tarot]读取%d组塔罗牌阵", len(formationMap))
|
||||
return true
|
||||
})
|
||||
engine.OnRegex(`^抽(\d{1,2}张)?塔罗牌$`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
match := ctx.State["regex_matched"].([]string)[1]
|
||||
n := 1
|
||||
reasons := [...]string{"您抽到的是~\n", "锵锵锵,塔罗牌的预言是~\n", "诶,让我看看您抽到了~\n"}
|
||||
@@ -118,38 +145,7 @@ func init() {
|
||||
ctx.SendGroupForwardMessage(ctx.Event.GroupID, msg)
|
||||
})
|
||||
|
||||
engine.OnRegex(`^解塔罗牌\s?(.*)`, ctxext.DoOnceOnSuccess(
|
||||
func(ctx *zero.Ctx) bool {
|
||||
if len(cardMap) > 0 {
|
||||
for _, card := range cardMap {
|
||||
infoMapKey := strings.Split(card.Name, "(")[0]
|
||||
infoMap[infoMapKey] = card.cardInfo
|
||||
// 可以拿来显示大阿尔卡纳列表
|
||||
// cardName = append(cardName, infoMapKey)
|
||||
}
|
||||
return true
|
||||
}
|
||||
tempMap := make(cardSet, 30)
|
||||
data, err := engine.GetLazyData("tarots.json", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
err = json.Unmarshal(data, &tempMap)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
|
||||
for _, card := range tempMap {
|
||||
infoMapKey := strings.Split(card.Name, "(")[0]
|
||||
infoMap[infoMapKey] = card.cardInfo
|
||||
// 可以拿来显示大阿尔卡纳列表
|
||||
// cardName = append(cardName, infoMapKey)
|
||||
}
|
||||
return true
|
||||
},
|
||||
)).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
engine.OnRegex(`^解塔罗牌\s?(.*)`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
match := ctx.State["regex_matched"].([]string)[1]
|
||||
info, ok := infoMap[match]
|
||||
if ok {
|
||||
@@ -162,4 +158,48 @@ func init() {
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~"))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^塔罗牌阵\s?(.*)`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
match := ctx.State["regex_matched"].([]string)[1]
|
||||
info, ok := formationMap[match]
|
||||
position := [...]string{"正位", "逆位"}
|
||||
reverse := [...]string{"", "Reverse"}
|
||||
if ok {
|
||||
var build strings.Builder
|
||||
build.WriteString(ctx.CardOrNickName(ctx.Event.UserID))
|
||||
build.WriteString("\n")
|
||||
msg := make([]message.MessageSegment, info.CardsNum)
|
||||
randomIntMap := make(map[int]int, 30)
|
||||
for i := range msg {
|
||||
j := rand.Intn(22)
|
||||
_, ok := randomIntMap[j]
|
||||
for ok {
|
||||
j = rand.Intn(22)
|
||||
_, ok = randomIntMap[j]
|
||||
}
|
||||
randomIntMap[j] = 0
|
||||
p := rand.Intn(2)
|
||||
card := cardMap[(strconv.Itoa(j))]
|
||||
name := card.Name
|
||||
tarotMsg := []message.MessageSegment{message.Image(fmt.Sprintf(bed+"MajorArcana%s/%d.png", reverse[p], j))}
|
||||
build.WriteString(info.Represent[0][i])
|
||||
build.WriteString(": ")
|
||||
build.WriteString(position[p])
|
||||
build.WriteString(" 的 ")
|
||||
build.WriteString(name)
|
||||
build.WriteString("\n")
|
||||
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotMsg...)
|
||||
}
|
||||
txt := build.String()
|
||||
formation, err := text.RenderToBase64(txt, text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
// TODO 视gocq变化将牌阵信息加入转发列表中
|
||||
ctx.SendChain(message.Image("base64://" + binary.BytesToString(formation)))
|
||||
ctx.SendGroupForwardMessage(ctx.Event.GroupID, msg)
|
||||
} else {
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Package zaobao 易即今日公众号api的今日早报
|
||||
package zaobao
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
)
|
||||
|
||||
const (
|
||||
api = "http://api.soyiji.com/news_jpg"
|
||||
referer = "safe.soyiji.com"
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66"
|
||||
)
|
||||
|
||||
var (
|
||||
picdata []byte
|
||||
mu sync.Mutex
|
||||
pictime time.Time
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("zaobao", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: true,
|
||||
Help: "易即今日公众号api的今日早报\n" +
|
||||
"api早上8点更新,推荐定时在8点30后\n" +
|
||||
"配合插件job中的记录在\"cron\"触发的指令使用\n" +
|
||||
"- 记录在\"0 9 * * *\"触发的指令\n" +
|
||||
" - 今日早报",
|
||||
})
|
||||
engine.OnFullMatch("今日早报", zero.OnlyGroup).SetBlock(false).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
err := getdata()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.ImageBytes(picdata))
|
||||
})
|
||||
}
|
||||
|
||||
func getdata() error { // 获取图片链接并且下载
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if picdata != nil && time.Since(pictime) <= time.Hour*8 && time.Now().Day() == pictime.Day() {
|
||||
return nil
|
||||
}
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), api, "GET", "", ua)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
picdata, err = web.RequestDataWith(web.NewDefaultClient(), gjson.Get(binary.BytesToString(data), "url").String(), "GET", referer, ua)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pictime = time.Now()
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user