Compare commits

...

14 Commits

Author SHA1 Message Date
源文雨
7e7191536a 🔖 v1.5.0-beta2 2022-07-03 18:28:18 +08:00
时雨◎星空
16ff1975ff 修复猜歌插件创建config.json错误 (#296) 2022-07-03 18:20:04 +08:00
源文雨
b32c86863e edit main 2022-07-03 10:24:33 +08:00
源文雨
d9b361f765 🔖 v1.5.0-beta1 2022-07-03 09:52:01 +08:00
Fox_white
5b2810e6c5 为猜歌插件添加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>
2022-07-03 09:25:14 +08:00
github-actions[bot]
15a5b347e8 🎨 改进代码样式 2022-06-29 12:58:24 +00:00
方柳煜
477ffa825b 优化qqwife插件,新增猜歌插件 (#283)
* 猜歌插件

* 猜歌插件

* 猜歌插件

* 优化重置花名册指令

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update main.go

* Update README.md
2022-06-29 20:57:58 +08:00
Jiang-Red
84363b1513 删除无法使用的zaobao (#278)
* drop zaobao

* edit readme

* drop zaobao
2022-06-24 21:00:55 +08:00
方柳煜
01bf6f049b 修复存在误解的文案 (#272)
* 修复存在误解的文案

* Update qqmapwife.go
2022-06-19 20:51:47 +08:00
莫思潋
a8d93de000 feat:塔罗牌阵 (#268)
* 优化在两个命令中使用空格分隔的体验
- fortune的设置底图功能
- b14的加密功能

* 优化四个插件中使用空格分隔的体验
- 加密
- 哔哩哔哩推送
- 藏头诗
- 运势

* 优化并修正了上一个commit
- 加上了因为复制粘贴疏忽又没有注意测试遗漏的`?`
- 调整藏头诗和加密的正则触发,使其不必多此一举
- 删去了未被发现的测试代码

* - 删去了遗漏的Trim

* 优化了更多插件中使用空格的体验
- 优化了music bilibili image_finder 中使用空格的体验
- 补上了plugin_bilibili中未实现的vup开头触发
- 为plugin_bilibili_parse输出的消息加上一个换行符,优化排版

* - 调整funny
- 补回readme中bilibili_push的注释说明

* - 简化funny说明

* - 用暴力的办法使抽多张塔罗牌不重复
- 逆位的图片现在可以倒过来了

* - 解塔罗牌

* - 修改readme

* 大幅减少全局变量,简化抽塔罗牌输出

* - 更新了塔罗牌阵

* - 顺lint心意
- 添加判断避免重复装填

* 调整塔罗牌阵消息
- 用图片说明牌阵信息避免被封

* 调整DoOnceOnSuccess,减少冗余

* 🎨 改进代码样式

* 调整发送牌阵信息

* 🎨 改进代码样式

* 改回base64

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-06-18 16:51:11 +08:00
github-actions[bot]
2da5de3743 🎨 改进代码样式 2022-06-18 04:06:33 +00:00
方柳煜
bf6bac7be6 修复花名册会显示单身贵族的问题 (#269)
* 修复花名册会显示单身贵族的问题

* Update qqmapwife.go

* Update qqmapwife.go
2022-06-18 12:06:07 +08:00
源文雨
394d2e4a08 edit README 2022-06-17 11:59:26 +08:00
himawari
0dd447e270 添加txt转midi,优化midi转txt (#267)
* 🐛 长字符串发文件

*  添加txt转midi

*  音色自定义

*  添加windows脚本说明

* 🐛 修复某些midi读取不了的问题

*  多音轨转换

Co-authored-by: haibaraguo <haibaraguo@yeahka.com>
2022-06-17 11:46:09 +08:00
11 changed files with 1000 additions and 226 deletions

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -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",
}

View File

@@ -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
View 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)只会取到0rand不允许的
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
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"`
}

View File

@@ -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
}

View File

@@ -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中)"))
}

View File

@@ -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, "噢~"))
}
})
}

View File

@@ -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
}