mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-07 07:40:21 +00:00
Compare commits
40 Commits
v1.5.0-bet
...
v1.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
926d48af81 | ||
|
|
962c48ca36 | ||
|
|
03fc569f87 | ||
|
|
e5b712ff39 | ||
|
|
c7f7d0409e | ||
|
|
e4ec7ea5bb | ||
|
|
4bf734778b | ||
|
|
461325c8ca | ||
|
|
efa2ec6aac | ||
|
|
eb8be40f77 | ||
|
|
ae7e3c9c89 | ||
|
|
4d68cc9fce | ||
|
|
0226e2739a | ||
|
|
caa163a879 | ||
|
|
0e29010897 | ||
|
|
e9a8b99f4f | ||
|
|
27c637ba66 | ||
|
|
305db3395c | ||
|
|
3da37f0a3e | ||
|
|
c30c9192f4 | ||
|
|
ceb3df513d | ||
|
|
83372c4209 | ||
|
|
20d5e014b7 | ||
|
|
1c9a7eb158 | ||
|
|
31555cbf2f | ||
|
|
4a1d4644ed | ||
|
|
9182d214af | ||
|
|
45e3383031 | ||
|
|
fc91b69b55 | ||
|
|
13b6614dc2 | ||
|
|
fec5e4d73f | ||
|
|
585add29fc | ||
|
|
1e48e59cb7 | ||
|
|
41911af09a | ||
|
|
37b95e9559 | ||
|
|
a7901745e8 | ||
|
|
da9f085a50 | ||
|
|
192c2a8bd6 | ||
|
|
e6c00e9b68 | ||
|
|
1faa5aa5d8 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -5,4 +5,5 @@ plugins/*.dll
|
||||
.vscode
|
||||
go-zero*
|
||||
nohup.out
|
||||
zerobot
|
||||
zerobot
|
||||
ZeroBot-Plugin*
|
||||
|
||||
132
README.md
132
README.md
@@ -80,6 +80,14 @@ zerobot [-c config.json] [-h] [-s config.json] [-t token] [-u url] [-n nickname]
|
||||
<details>
|
||||
<summary>插件控制</summary>
|
||||
|
||||
- [x] /响应 (在发送的群/用户开始工作)
|
||||
|
||||
- [x] /沉默 (在发送的群/用户停止工作)
|
||||
|
||||
- [x] /全局响应 (在所有位置开始工作,无视单独的沉默)
|
||||
|
||||
- [x] /全局沉默 (在所有本应沉默的位置停止工作,显式指定启用的位置不受影响)
|
||||
|
||||
- [x] /启用 xxx (在发送的群/用户启用xxx)
|
||||
|
||||
- [x] /禁用 xxx (在发送的群/用户禁用xxx)
|
||||
@@ -114,7 +122,7 @@ zerobot [-c config.json] [-h] [-s config.json] [-t token] [-u url] [-n nickname]
|
||||
<details>
|
||||
<summary>动态加载插件</summary>
|
||||
|
||||
`import _ github.com/FloatTech/ZeroBot-Plugin-Dynamic/dyloader`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin-Dynamic/dyloader"`
|
||||
|
||||
- 本功能需要`cgo`,故已分离出主线。详见[ZeroBot-Plugin-Dynamic](https://github.com/FloatTech/ZeroBot-Plugin-Dynamic)
|
||||
|
||||
@@ -142,7 +150,7 @@ zerobot [-c config.json] [-h] [-s config.json] [-t token] [-u url] [-n nickname]
|
||||
<details>
|
||||
<summary>睡眠管理</summary>
|
||||
|
||||
`import _ github.com/FloatTech/ZeroBot-Plugin/plugin/sleep_manage`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleep_manage"`
|
||||
|
||||
- [x] 早安 | 晚安
|
||||
|
||||
@@ -379,11 +387,29 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>b站视频链接解析</summary>
|
||||
<summary>b站动态、专栏、视频、直播解析</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_parse"`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili"`
|
||||
|
||||
- [x] https://www.bilibili.com/video/BV1xx411c7BF | https://www.bilibili.com/video/av1605 | https://b23.tv/I8uzWCA | https://www.bilibili.com/video/bv1xx411c7BF
|
||||
- [x] t.bilibili.com/642277677329285174 | bilibili.com/read/cv17134450 | bilibili.com/video/BV13B4y1x7pS | live.bilibili.com/22603245
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>b站动态、直播推送,需要配合job一起使用</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili"`
|
||||
|
||||
- [x] 添加b站订阅[uid|name]
|
||||
|
||||
- [x] 取消b站订阅[uid|name]
|
||||
|
||||
- [x] 取消b站动态订阅[uid|name]
|
||||
|
||||
- [x] 取消b站直播订阅[uid|name]
|
||||
|
||||
- [x] b站推送列表
|
||||
|
||||
- [x] 拉取b站推送 (使用job执行定时任务------记录在"@every 10s"触发的指令)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -405,6 +431,14 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 藏尾诗[xxx]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>英文字符翻转</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/char_reverser"`
|
||||
|
||||
- [x] 翻转 [英文字符串]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>选择困难症帮手</summary>
|
||||
@@ -443,7 +477,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
<details>
|
||||
<summary>DeepDanbooru二次元图标签识别</summary>
|
||||
|
||||
`import _ github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru"`
|
||||
|
||||
- [x] 鉴赏图片[图片]
|
||||
|
||||
@@ -518,7 +552,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
<details>
|
||||
<summary>每日运势</summary>
|
||||
|
||||
`import _ github.com/FloatTech/ZeroBot-Plugin/plugin/fortune`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune"`
|
||||
|
||||
- [x] 运势 | 抽签
|
||||
|
||||
@@ -536,7 +570,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
<details>
|
||||
<summary>原神抽卡</summary>
|
||||
|
||||
`import _ github.com/FloatTech/ZeroBot-Plugin/plugin/genshin`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/genshin"`
|
||||
|
||||
- [x] 切换原神卡池
|
||||
|
||||
@@ -573,22 +607,32 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/guessmusic"`
|
||||
|
||||
- 猜歌插件(该插件依赖ffmpeg)
|
||||
|
||||
- [x] 个人猜歌
|
||||
|
||||
- [x] 团队猜歌
|
||||
|
||||
- [x] 设置猜歌缓存歌库路径 [绝对路径]
|
||||
|
||||
- [x] 设置猜歌本地 [true/false]
|
||||
|
||||
- [x] 设置猜歌Api [true/false]
|
||||
|
||||
- 注:默认歌库为网易云热歌榜
|
||||
- [x] 设置猜歌[本地/Api] [true/false]
|
||||
|
||||
- 1.可在后面添加“-动漫”进行动漫歌猜歌(这个只能猜歌名和歌手)
|
||||
- [x] 登录网易云
|
||||
|
||||
- 2.可在后面添加“-动漫2”进行动漫歌猜歌(这个可以猜番名,但歌手经常“未知”)
|
||||
- 注:不登陆也能用,API有几率返回400
|
||||
|
||||
- [x] 添加歌单 [网易云歌单ID] [歌单名称]
|
||||
|
||||
- 注:[歌单名称]可为空,默认原标题
|
||||
|
||||
- [x] 删除歌单 [网易云歌单ID/API歌单名称]
|
||||
|
||||
- [x] 获取歌单列表
|
||||
|
||||
- [x] [网易云歌单ID/API歌单名称]歌单信息
|
||||
|
||||
- [x] [个人/团队]猜歌
|
||||
|
||||
- 注:默认歌库为网易云ACG动画榜
|
||||
|
||||
- 可在后面添加[-歌单名称]进行指定歌单猜歌
|
||||
|
||||
- 歌单的歌曲命名规则为:歌名 - 歌手 - 其他(歌曲出处之类)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -680,10 +724,20 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [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] 注: 该插件需要安装timidity, linux安装脚本可参考 https://gitcode.net/anto_july/midi/-/raw/master/timidity.sh, windows安装脚本可参考 https://gitcode.net/anto_july/midi/-/raw/master/timidity.bat?inline=false, windows需要管理员模式运行
|
||||
|
||||
- [x] 符号说明: C5是中央C,后面不写数字,默认接5,Cb6<1,b代表降调,#代表升调,6比5高八度,<1代表音长×2,<3代表音长×8,<-1代表音长×0.5,<-3代表音长×0.125,R是休止符
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>日韩 VITS 模型拟声</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe"`
|
||||
|
||||
- [x] 让[宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海]说(日语)
|
||||
|
||||
- [x] 让[수아|미미르|아린|연화|유화|선배]说(韩语)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>摸鱼</summary>
|
||||
@@ -770,6 +824,14 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] ?? [缩写]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>日语语法学习</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nihongo"`
|
||||
|
||||
- [x] 日语语法 [xxx] (使用tag随机)
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>小说</summary>
|
||||
@@ -792,7 +854,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
<details>
|
||||
<summary>浅草寺求签</summary>
|
||||
|
||||
`import _ github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji`
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji"`
|
||||
|
||||
- [x] 求签 | 占卜
|
||||
|
||||
@@ -883,10 +945,10 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/tarot"`
|
||||
|
||||
- [x] 抽塔罗牌
|
||||
- [x] 抽n张塔罗牌
|
||||
- [x] 抽[塔罗牌|大阿卡纳|小阿卡纳]
|
||||
- [x] 抽n张[塔罗牌|大阿卡纳|小阿卡纳]
|
||||
- [x] 解塔罗牌[牌名]
|
||||
- [x] 塔罗牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]"
|
||||
- [x] [塔罗|大阿卡纳|小阿卡纳|混合]牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -906,6 +968,10 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 团队猜单词
|
||||
|
||||
- [x] 团队六阶猜单词
|
||||
|
||||
- [x] 团队七阶猜单词
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>翻译</summary>
|
||||
@@ -978,24 +1044,6 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- 注:由于需要科学,默认注释。
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>b站推送</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_push"`
|
||||
|
||||
- [x] 添加b站订阅[uid]
|
||||
|
||||
- [x] 取消b站订阅[uid]
|
||||
|
||||
- [x] 取消b站动态订阅[uid]
|
||||
|
||||
- [x] 取消b站直播订阅[uid]
|
||||
|
||||
- [x] b站推送列表
|
||||
|
||||
- 注:由于需要安装Chrome,默认注释,具体看[这里](https://www.yuque.com/xiangrikuidezhongzi/zerobot/qrwxth)
|
||||
|
||||
</details>
|
||||
|
||||
### *低优先级*
|
||||
|
||||
27
go.mod
27
go.mod
@@ -3,15 +3,15 @@ module github.com/FloatTech/ZeroBot-Plugin
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/FloatTech/AnimeAPI v1.4.1-0.20220613042537-0adf8c5616ec
|
||||
github.com/FloatTech/sqlite v0.3.2
|
||||
github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27
|
||||
github.com/FloatTech/zbputils v1.4.1-0.20220613042833-33e22060e8d9
|
||||
github.com/Coloured-glaze/gg v1.3.2
|
||||
github.com/FloatTech/AnimeAPI v1.5.1-0.20220816023211-5e11b0d21958
|
||||
github.com/FloatTech/sqlite v0.3.3
|
||||
github.com/FloatTech/zbpctrl v1.4.1-0.20220715042842-93f081cb0133
|
||||
github.com/FloatTech/zbputils v1.5.1-0.20220816020252-0f57a5ae28aa
|
||||
github.com/antchfx/htmlquery v1.2.5
|
||||
github.com/corona10/goimagehash v1.0.3
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/fumiama/cron v1.3.0
|
||||
github.com/fumiama/go-base16384 v1.5.3
|
||||
github.com/fumiama/go-base16384 v1.5.4
|
||||
github.com/fumiama/go-registry v0.1.6
|
||||
github.com/fumiama/gofastTEA v0.0.10
|
||||
github.com/fumiama/gotracemoe v0.0.3
|
||||
@@ -24,15 +24,16 @@ require (
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/pkumza/numcn v1.0.0
|
||||
github.com/shirou/gopsutil/v3 v3.22.3
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/tidwall/gjson v1.14.1
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/tidwall/gjson v1.14.2
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220715040337-ef4327320c40
|
||||
gitlab.com/gomidi/midi/v2 v2.0.17
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect
|
||||
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c // indirect
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220123145318-307a86b127bc // indirect
|
||||
github.com/antchfx/xpath v1.2.1 // indirect
|
||||
@@ -64,13 +65,13 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
|
||||
golang.org/x/mod v0.4.2 // indirect
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 // indirect
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 // indirect
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
golang.org/x/tools v0.1.1 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
modernc.org/libc v1.16.8 // indirect
|
||||
modernc.org/libc v1.16.19 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
modernc.org/memory v1.1.1 // indirect
|
||||
)
|
||||
|
||||
67
go.sum
67
go.sum
@@ -8,14 +8,18 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1
|
||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/FloatTech/AnimeAPI v1.4.1-0.20220613042537-0adf8c5616ec h1:Hl/BxaQoQFEJUIGmdhR0NyBvZChb7qkfCrF916bNxnI=
|
||||
github.com/FloatTech/AnimeAPI v1.4.1-0.20220613042537-0adf8c5616ec/go.mod h1:MrCZ7P6WvF0JWHMeedAx6fVm5BP1s6/RUy0r0Jvqrds=
|
||||
github.com/FloatTech/sqlite v0.3.2 h1:iTg2ZKnzjjZAdlSN3hXmpCBn15odc4Ud484OoM3yXGA=
|
||||
github.com/FloatTech/sqlite v0.3.2/go.mod h1:VFtLofV5qxw5eBneZRbWwD451SLSm50o9J3J43iB1iw=
|
||||
github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27 h1:C+D30vpxfgbJetTFXWAHzuU8GydbFb/A8Kv6E3PdRS4=
|
||||
github.com/FloatTech/zbpctrl v1.4.1-0.20220610074608-425160596f27/go.mod h1:5FDkrlVaQCxUfeqH7XJPTfej0q+y9fzImhvZI4ofu9Y=
|
||||
github.com/FloatTech/zbputils v1.4.1-0.20220613042833-33e22060e8d9 h1:IeUs08sUqdR/g8DfxPNTSBfsE1g0OtmffLNgKzbrVZ4=
|
||||
github.com/FloatTech/zbputils v1.4.1-0.20220613042833-33e22060e8d9/go.mod h1:A9AeVHZsv5chyw8p4fDI0cHnEOfMpmsTLoLWqUT7TO4=
|
||||
github.com/Coloured-glaze/gg v1.3.2 h1:HRWF0qoIxwhO1O0N5eN2eTPwaO+xHpFu1FfNhtls22w=
|
||||
github.com/Coloured-glaze/gg v1.3.2/go.mod h1:Ih5NLNNDHOy3RJbB0EPqGTreIzq/H02TGThIagh8HJg=
|
||||
github.com/FloatTech/AnimeAPI v1.5.1-0.20220816023211-5e11b0d21958 h1:BXjlcvPK/7IwaW1HTEPt/pMvbPrc3id1NfFkJ+lh1e4=
|
||||
github.com/FloatTech/AnimeAPI v1.5.1-0.20220816023211-5e11b0d21958/go.mod h1:vhrzmpfFp9p4V1T+xY2OaFUAx498LmMxdk/UqJmPMTc=
|
||||
github.com/FloatTech/sqlite v0.3.3 h1:cSlpv+DcIehqaUG2YyjW0Twh396FoGsybpEpFZnX3Jg=
|
||||
github.com/FloatTech/sqlite v0.3.3/go.mod h1:i33d92OtR8jcp5fBUvQtospf27+MkfUxnGwnZ95E/dA=
|
||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw=
|
||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/FloatTech/zbpctrl v1.4.1-0.20220715042842-93f081cb0133 h1:nP9NI4I+vtwAbiU7wCJwjuzCzMZ/yJYg8h3667HGnv0=
|
||||
github.com/FloatTech/zbpctrl v1.4.1-0.20220715042842-93f081cb0133/go.mod h1:72BnjyBwQWUC8mqM9dPk5ZrjxXCilQCVp+jfgHATNdw=
|
||||
github.com/FloatTech/zbputils v1.5.1-0.20220816020252-0f57a5ae28aa h1:nltmv5w1cdB0vU52zkLFI9a5FdBozaAhZZq/GCLi6Xs=
|
||||
github.com/FloatTech/zbputils v1.5.1-0.20220816020252-0f57a5ae28aa/go.mod h1:l06shz0lp4jqMBZDJaRRSUdOlIwg99cIKV4sx4YR0DI=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c h1:cNPOdTNiVwxLpROLjXCgbIPvdkE+BwvxDvgmdYmWx6Q=
|
||||
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c/go.mod h1:KqZzu7slNKROh3TSYEH/IUMG6f4M+1qubZ5e52QypsE=
|
||||
@@ -49,16 +53,14 @@ github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4/go.mod h1:H
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
|
||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo=
|
||||
github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY=
|
||||
github.com/fumiama/go-base16384 v1.5.3 h1:IUZUlm2ajJB1nEJzauP6yD5IeJoVHyBEkzKJf9O82zs=
|
||||
github.com/fumiama/go-base16384 v1.5.3/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-base16384 v1.5.4 h1:UKx925X7cTsqsVWBlBLBs1v38epFT/q3AGjvyP5E0PM=
|
||||
github.com/fumiama/go-base16384 v1.5.4/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-registry v0.1.6 h1:Ee/tXCCIR/xt8celhbbw0W/xDMdhAXLwy2YFBB/LWFk=
|
||||
github.com/fumiama/go-registry v0.1.6/go.mod h1:dIUVbiOgfk9oZcsgwDvNLC72i+ctibVukSXR/9bLviI=
|
||||
github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ=
|
||||
@@ -229,18 +231,19 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l
|
||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
|
||||
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
@@ -253,8 +256,8 @@ github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49u
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0 h1:tY2slqVQ6bN+yHSnDYwZebLQFkphK4WNrVwnt7CJZ2I=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.0/go.mod h1:yx7MvAVNcP/kN9lKXM/NTce4au4DFN99j6i1OwDclNA=
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee h1:b2f+KLhZv+BCMQZuwMJvhKQOrz5YXzOduHC3G1DjQR0=
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220610070647-9eeffcb277ee/go.mod h1:LJ+VOf523i3IrykuLK53UEeWqnAclRL5d2wGT4sS4Zk=
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220715040337-ef4327320c40 h1:j+cmfkjc8WcBKiOsvMzcIgAuPhiTs7FBeiRn/QLVFhA=
|
||||
github.com/wdvxdr1123/ZeroBot v1.5.2-0.20220715040337-ef4327320c40/go.mod h1:LJ+VOf523i3IrykuLK53UEeWqnAclRL5d2wGT4sS4Zk=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
|
||||
@@ -276,8 +279,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd h1:9NbNcTg//wfC5JskFW4Z3sqwVnjmJKHxLAol1bW2qgw=
|
||||
golang.org/x/image v0.0.0-20220601225756-64ec528b34cd/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -304,8 +307,8 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48 h1:N9Vc/rorQUDes6B9CNdIxAn5jODGj2wzfrei2x4wNj4=
|
||||
golang.org/x/net v0.0.0-20220805013720-a33c5aa5df48/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -327,7 +330,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -348,8 +350,9 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68 h1:z8Hj/bl9cOV2grsOpEaQFUaly0JWN3i97mo3jXKJNp0=
|
||||
golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418 h1:9vYwv7OjYaky/tlAeD7C4oC9EsPTlaFl1H2jS++V+ME=
|
||||
golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -436,9 +439,6 @@ modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g
|
||||
modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
|
||||
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
|
||||
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
|
||||
modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
|
||||
modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
|
||||
modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
|
||||
@@ -480,11 +480,8 @@ modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi
|
||||
modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
|
||||
modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
|
||||
modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
|
||||
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
|
||||
modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
|
||||
modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
|
||||
modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
|
||||
modernc.org/libc v1.11.0/go.mod h1:2lOfPmj7cz+g1MrPNmX65QCzVxgNq2C5o0jdLY2gAYg=
|
||||
@@ -527,10 +524,8 @@ modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
|
||||
modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
|
||||
modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
|
||||
modernc.org/libc v1.14.5/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
|
||||
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
|
||||
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
|
||||
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
|
||||
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
|
||||
modernc.org/libc v1.16.19 h1:S8flPn5ZeXx6iw/8yNa986hwTQDrY8RXU7tObZuAozo=
|
||||
modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
|
||||
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
var (
|
||||
info = [...]string{
|
||||
"* OneBot + ZeroBot + Golang",
|
||||
"* Version 1.5.0-beta2 - 2022-07-03 18:24:34 +0800 CST",
|
||||
"* Version 1.5.0-beta5 - 2022-07-22 15:39:17 +0800 CST",
|
||||
"* Copyright © 2020 - 2022 FloatTech. All Rights Reserved.",
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
|
||||
}
|
||||
|
||||
120
main.go
120
main.go
@@ -55,65 +55,67 @@ import (
|
||||
// vvvvvvvvvvvvvv //
|
||||
// vvvv //
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ai_false" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // 查询b站用户信息
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili_parse" // b站视频链接解析
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/book_review" // 哀伤雪刃吧推书记录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/epidemic" // 城市疫情查询
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
|
||||
_ "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" // 关键字搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar" // 摸鱼人日历
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife" // 本地老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/scale" // 叔叔的AI二次元图片放大
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime" // 来份涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shadiao" // 沙雕app
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shindan" // 测定
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tarot" // 抽塔罗牌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
|
||||
_ "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/ai_false" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/book_review" // 哀伤雪刃吧推书记录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cangtoushi" // 藏头诗
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/char_reverser" // 英文字符翻转
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/choose" // 选择困难症帮手
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chouxianghua" // 说抽象话
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle" // 漂流瓶
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/emojimix" // 合成emoji
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/epidemic" // 城市疫情查询
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/font" // 渲染任意文字到图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/fortune" // 运势
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/funny" // 笑话
|
||||
_ "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" // 关键字搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/inject" // 注入指令
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/jandan" // 煎蛋网无聊图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/juejuezi" // 绝绝子生成器
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/lolicon" // lolicon 随机图片
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/midicreate" // 简易midi音乐制作
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moegoe" // 日韩 VITS 模型拟声
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu" // 摸鱼
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/moyu_calendar" // 摸鱼人日历
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/music" // 点歌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativesetu" // 本地涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nativewife" // 本地老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nbnhhsh" // 拼音首字母缩写释义工具
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nihongo" // 日语语法学习
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/novel" // 铅笔小说网搜索
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/nsfw" // nsfw图片识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/omikuji" // 浅草寺求签
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/qqwife" // 一群一天一夫一妻制群老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/reborn" // 投胎
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/runcode" // 在线运行代码
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/saucenao" // 以图搜图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/scale" // 叔叔的AI二次元图片放大
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/score" // 分数
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/setutime" // 来份涩图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shadiao" // 沙雕app
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/shindan" // 测定
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tarot" // 抽塔罗牌
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tiangou" // 舔狗日记
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/tracemoe" // 搜番
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/translation" // 翻译
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/vtb_quotation" // vtb语录
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/wangyiyun" // 网易云音乐热评
|
||||
_ "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/wtf" // 鬼东西
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ func init() {
|
||||
Help: "base16384加解密\n" +
|
||||
"- 加密xxx\n- 解密xxx\n- 用yyy加密xxx\n- 用yyy解密xxx",
|
||||
})
|
||||
en.OnRegex(`^加密\s*(.*)`).SetBlock(true).
|
||||
en.OnRegex(`^加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := base14.EncodeString(str)
|
||||
@@ -29,7 +29,7 @@ func init() {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^解密\s*([一-踀]*[㴁-㴆]?)$`).SetBlock(true).
|
||||
en.OnRegex(`^解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
str := ctx.State["regex_matched"].([]string)[1]
|
||||
es := base14.DecodeString(str)
|
||||
@@ -39,7 +39,7 @@ func init() {
|
||||
ctx.SendChain(message.Text("解密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^用(.*)加密\s*(.*)`).SetBlock(true).
|
||||
en.OnRegex(`^用(.+)加密\s*(.+)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := getea(key)
|
||||
@@ -50,7 +50,7 @@ func init() {
|
||||
ctx.SendChain(message.Text("加密失败!"))
|
||||
}
|
||||
})
|
||||
en.OnRegex(`^用(.*)解密\s*([一-踀]*[㴁-㴆]?)$`).SetBlock(true).
|
||||
en.OnRegex(`^用(.+)解密\s*([一-踀]+[㴁-㴆]?)$`).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
key, str := ctx.State["regex_matched"].([]string)[1], ctx.State["regex_matched"].([]string)[2]
|
||||
t := getea(key)
|
||||
|
||||
@@ -3,8 +3,9 @@ package bilibili
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
@@ -15,18 +16,9 @@ var (
|
||||
errNeedCookie = errors.New("该api需要设置b站cookie,请发送命令设置cookie,例如\"设置b站cookie SESSDATA=82da790d,1663822823,06ecf*31\"")
|
||||
)
|
||||
|
||||
type searchResult struct {
|
||||
Mid int64 `json:"mid"`
|
||||
Uname string `json:"uname"`
|
||||
Gender int64 `json:"gender"`
|
||||
Usign string `json:"usign"`
|
||||
Level int64 `json:"level"`
|
||||
}
|
||||
|
||||
// 搜索api:通过把触发指令传入的昵称找出uid返回
|
||||
func search(keyword string) (r []searchResult, err error) {
|
||||
searchURL := "http://api.bilibili.com/x/web-interface/search/type?search_type=bili_user&keyword=" + keyword
|
||||
data, err := web.GetData(searchURL)
|
||||
// searchUser 查找b站用户
|
||||
func searchUser(keyword string) (r []searchResult, err error) {
|
||||
data, err := web.GetData(fmt.Sprintf(searchUserURL, keyword))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -42,21 +34,9 @@ func search(keyword string) (r []searchResult, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type follower struct {
|
||||
Mid int `json:"mid"`
|
||||
Uname string `json:"uname"`
|
||||
Video int `json:"video"`
|
||||
Roomid int `json:"roomid"`
|
||||
Rise int `json:"rise"`
|
||||
Follower int `json:"follower"`
|
||||
GuardNum int `json:"guardNum"`
|
||||
AreaRank int `json:"areaRank"`
|
||||
}
|
||||
|
||||
// 请求api
|
||||
func fansapi(uid string) (result follower, err error) {
|
||||
fanURL := "https://api.vtbs.moe/v1/detail/" + uid
|
||||
data, err := web.GetData(fanURL)
|
||||
// getVtbDetail 查找vtb信息
|
||||
func getVtbDetail(uid string) (result vtbDetail, err error) {
|
||||
data, err := web.GetData(fmt.Sprintf(vtbDetailURL, uid))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -66,76 +46,9 @@ func fansapi(uid string) (result follower, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func followings(uid string) (s string, err error) {
|
||||
followingURL := "https://api.bilibili.com/x/relation/same/followings?vmid=" + uid
|
||||
method := "GET"
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(method, followingURL, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c := vdb.getBilibiliCookie()
|
||||
req.Header.Add("cookie", c.Value)
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
j := gjson.ParseBytes(body)
|
||||
s = j.Get("data.list.#.uname").Raw
|
||||
if j.Get("code").Int() == -101 {
|
||||
err = errNeedCookie
|
||||
return
|
||||
}
|
||||
if j.Get("code").Int() != 0 {
|
||||
err = errors.New(j.Get("message").String())
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type userinfo struct {
|
||||
Name string `json:"name"`
|
||||
Mid string `json:"mid"`
|
||||
Face string `json:"face"`
|
||||
Fans int64 `json:"fans"`
|
||||
Regtime int64 `json:"regtime"`
|
||||
Attentions []int64 `json:"attentions"`
|
||||
}
|
||||
|
||||
type medalInfo struct {
|
||||
Mid int64 `json:"target_id"`
|
||||
MedalName string `json:"medal_name"`
|
||||
Level int64 `json:"level"`
|
||||
MedalColorStart int64 `json:"medal_color_start"`
|
||||
MedalColorEnd int64 `json:"medal_color_end"`
|
||||
MedalColorBorder int64 `json:"medal_color_border"`
|
||||
}
|
||||
type medal struct {
|
||||
Uname string `json:"target_name"`
|
||||
medalInfo `json:"medal_info"`
|
||||
}
|
||||
|
||||
type medalSlice []medal
|
||||
|
||||
func (m medalSlice) Len() int {
|
||||
return len(m)
|
||||
}
|
||||
func (m medalSlice) Swap(i, j int) {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
}
|
||||
func (m medalSlice) Less(i, j int) bool {
|
||||
return m[i].Level > m[j].Level
|
||||
}
|
||||
|
||||
// 获取详情
|
||||
func card(uid string) (result userinfo, err error) {
|
||||
cardURL := "https://account.bilibili.com/api/member/getCardByMid?mid=" + uid
|
||||
data, err := web.GetData(cardURL)
|
||||
// getMemberCard 获取b站个人详情
|
||||
func getMemberCard(uid interface{}) (result memberCard, err error) {
|
||||
data, err := web.GetData(fmt.Sprintf(memberCardURL, uid))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -146,12 +59,10 @@ func card(uid string) (result userinfo, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获得牌子
|
||||
func medalwall(uid string) (result []medal, err error) {
|
||||
medalwallURL := "https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id=" + uid
|
||||
method := "GET"
|
||||
// getMedalwall 用b站uid获得牌子
|
||||
func getMedalwall(uid string) (result []medal, err error) {
|
||||
client := &http.Client{}
|
||||
req, err := http.NewRequest(method, medalwallURL, nil)
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf(medalwallURL, uid), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -162,18 +73,56 @@ func medalwall(uid string) (result []medal, err error) {
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
data, err := io.ReadAll(res.Body)
|
||||
var md medalData
|
||||
err = json.NewDecoder(res.Body).Decode(&md)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
j := gjson.ParseBytes(data)
|
||||
if j.Get("code").Int() == -101 {
|
||||
if md.Code == -101 {
|
||||
err = errNeedCookie
|
||||
return
|
||||
}
|
||||
if j.Get("code").Int() != 0 {
|
||||
err = errors.New(j.Get("message").String())
|
||||
if md.Code != 0 {
|
||||
err = errors.New(md.Message)
|
||||
}
|
||||
_ = json.Unmarshal(binary.StringToBytes(j.Get("data.list").Raw), &result)
|
||||
result = md.Data.List
|
||||
return
|
||||
}
|
||||
|
||||
// getArticleInfo 用id查专栏信息
|
||||
func getArticleInfo(id string) (card Card, err error) {
|
||||
var data []byte
|
||||
data, err = web.GetData(fmt.Sprintf(articleInfoURL, id))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("data").Raw), &card)
|
||||
return
|
||||
}
|
||||
|
||||
// getLiveRoomInfo 用直播间id查直播间信息
|
||||
func getLiveRoomInfo(roomID string) (card roomCard, err error) {
|
||||
var data []byte
|
||||
data, err = web.GetData(fmt.Sprintf(liveRoomInfoURL, roomID))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("data").Raw), &card)
|
||||
return
|
||||
}
|
||||
|
||||
// getVideoInfo 用av或bv查视频信息
|
||||
func getVideoInfo(id string) (card Card, err error) {
|
||||
var data []byte
|
||||
_, err = strconv.Atoi(id)
|
||||
if err == nil {
|
||||
data, err = web.GetData(fmt.Sprintf(videoInfoURL, id, ""))
|
||||
} else {
|
||||
data, err = web.GetData(fmt.Sprintf(videoInfoURL, "", id))
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(binary.StringToBytes(gjson.ParseBytes(data).Get("data").Raw), &card)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/Coloured-glaze/gg"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
@@ -21,33 +22,32 @@ import (
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/FloatTech/zbputils/img/writer"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
"github.com/fogleman/gg"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var engine = control.Register("bilibili", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibili\n" +
|
||||
"- >vup info [xxx]\n" +
|
||||
"- >user info [xxx]\n" +
|
||||
"- 查成分 [xxx]\n" +
|
||||
"- 设置b站cookie SESSDATA=82da790d,1663822823,06ecf*31\n" +
|
||||
"- 更新vup",
|
||||
PublicDataFolder: "Bilibili",
|
||||
})
|
||||
var re = regexp.MustCompile(`^\d+$`)
|
||||
|
||||
// 查成分的
|
||||
func init() {
|
||||
engine := control.Register("bilibili", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibili\n" +
|
||||
"- >vup info [xxx]\n" +
|
||||
"- >user info [xxx]\n" +
|
||||
"- 查成分 [xxx]\n" +
|
||||
"- 设置b站cookie SESSDATA=82da790d,1663822823,06ecf*31\n" +
|
||||
"- 更新vup",
|
||||
PublicDataFolder: "Bilibili",
|
||||
})
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
_ = os.RemoveAll(cachePath)
|
||||
_ = os.MkdirAll(cachePath, 0755)
|
||||
var getdb = ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
var err error
|
||||
_, _ = engine.GetLazyData("bilibili.db", false)
|
||||
vdb, err = initialize(engine.DataFolder() + "bilibili.db")
|
||||
vdb, err = initializeVup(engine.DataFolder() + "bilibili.db")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
@@ -55,46 +55,35 @@ func init() {
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnRegex(`^>user info\s?(.{1,25})$`, getdb).SetBlock(true).
|
||||
engine.OnRegex(`^>user info\s?(.{1,25})$`, getPara).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
uidRes, err := search(keyword)
|
||||
id := ctx.State["uid"].(string)
|
||||
card, err := getMemberCard(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
id := strconv.FormatInt(uidRes[0].Mid, 10)
|
||||
follwings, err := followings(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
ctx.SendChain(message.Text(
|
||||
"search: ", uidRes[0].Mid, "\n",
|
||||
"name: ", uidRes[0].Uname, "\n",
|
||||
"sex: ", []string{"", "男", "女", "未知"}[uidRes[0].Gender], "\n",
|
||||
"sign: ", uidRes[0].Usign, "\n",
|
||||
"level: ", uidRes[0].Level, "\n",
|
||||
"follow: ", follwings,
|
||||
"uid: ", card.Mid, "\n",
|
||||
"name: ", card.Name, "\n",
|
||||
"sex: ", card.Sex, "\n",
|
||||
"sign: ", card.Sign, "\n",
|
||||
"level: ", card.LevelInfo.CurrentLevel, "\n",
|
||||
"birthday: ", card.Birthday,
|
||||
))
|
||||
})
|
||||
|
||||
engine.OnRegex(`^>vup info\s?(.{1,25})$`).SetBlock(true).
|
||||
engine.OnRegex(`^>vup info\s?(.{1,25})$`, getPara).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
res, err := search(keyword)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
id := strconv.FormatInt(res[0].Mid, 10)
|
||||
id := ctx.State["uid"].(string)
|
||||
// 获取详情
|
||||
fo, err := fansapi(id)
|
||||
fo, err := getVtbDetail(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text(
|
||||
"search: ", fo.Mid, "\n",
|
||||
"b站id: ", fo.Mid, "\n",
|
||||
"名字: ", fo.Uname, "\n",
|
||||
"当前粉丝数: ", fo.Follower, "\n",
|
||||
"24h涨粉数: ", fo.Rise, "\n",
|
||||
@@ -116,7 +105,7 @@ func init() {
|
||||
ctx.SendChain(message.Image("file:///" + file.BOTPATH + "/" + drawedFile))
|
||||
return
|
||||
}
|
||||
u, err := card(id)
|
||||
u, err := getMemberCard(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
@@ -127,7 +116,7 @@ func init() {
|
||||
return
|
||||
}
|
||||
vupLen := len(vups)
|
||||
medals, err := medalwall(id)
|
||||
medals, err := getMedalwall(id)
|
||||
sort.Sort(medalSlice(medals))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
@@ -312,7 +301,7 @@ func int2rbg(t int64) (int64, int64, int64) {
|
||||
func getPara(ctx *zero.Ctx) bool {
|
||||
keyword := ctx.State["regex_matched"].([]string)[1]
|
||||
if !re.MatchString(keyword) {
|
||||
searchRes, err := search(keyword)
|
||||
searchRes, err := searchUser(keyword)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
@@ -345,7 +334,7 @@ func getPara(ctx *zero.Ctx) bool {
|
||||
ctx.State["uid"] = keyword
|
||||
return true
|
||||
} else if num == 1 {
|
||||
searchRes, err := search(keyword)
|
||||
searchRes, err := searchUser(keyword)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
106
plugin/bilibili/bilibili_parse.go
Normal file
106
plugin/bilibili/bilibili_parse.go
Normal file
@@ -0,0 +1,106 @@
|
||||
// Package bilibili bilibili卡片解析
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
limit = ctxext.NewLimiterManager(time.Second*10, 1)
|
||||
searchVideo = `bilibili.com\\?/video\\?/(?:av(\d+)|([bB][vV][0-9a-zA-Z]+))`
|
||||
searchDynamic = `(t.bilibili.com|m.bilibili.com\\?/dynamic)\\?/(\d+)`
|
||||
searchArticle = `bilibili.com\\?/read\\?/(?:cv|mobile\\?/)(\d+)`
|
||||
searchLiveRoom = `live.bilibili.com\\?/(\d+)`
|
||||
searchVideoRe = regexp.MustCompile(searchVideo)
|
||||
searchDynamicRe = regexp.MustCompile(searchDynamic)
|
||||
searchArticleRe = regexp.MustCompile(searchArticle)
|
||||
searchLiveRoomRe = regexp.MustCompile(searchLiveRoom)
|
||||
)
|
||||
|
||||
// 插件主体
|
||||
func init() {
|
||||
en := control.Register("bilibiliparse", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "b站动态、专栏、视频、直播解析\n" +
|
||||
"- t.bilibili.com/642277677329285174 | bilibili.com/read/cv17134450 | bilibili.com/video/BV13B4y1x7pS | live.bilibili.com/22603245 ",
|
||||
})
|
||||
en.OnRegex(`((b23|acg).tv|bili2233.cn)/[0-9a-zA-Z]+`).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
url := ctx.State["regex_matched"].([]string)[0]
|
||||
realurl, err := getrealurl("https://" + url)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
switch {
|
||||
case searchVideoRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchVideoRe.FindStringSubmatch(realurl)
|
||||
handleVideo(ctx)
|
||||
case searchDynamicRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchDynamicRe.FindStringSubmatch(realurl)
|
||||
handleDynamic(ctx)
|
||||
case searchArticleRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchArticleRe.FindStringSubmatch(realurl)
|
||||
handleArticle(ctx)
|
||||
case searchLiveRoomRe.MatchString(realurl):
|
||||
ctx.State["regex_matched"] = searchLiveRoomRe.FindStringSubmatch(realurl)
|
||||
handleLive(ctx)
|
||||
}
|
||||
})
|
||||
en.OnRegex(searchVideo).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleVideo)
|
||||
en.OnRegex(searchDynamic).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleDynamic)
|
||||
en.OnRegex(searchArticle).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleArticle)
|
||||
en.OnRegex(searchLiveRoom).SetBlock(true).Limit(limit.LimitByGroup).Handle(handleLive)
|
||||
}
|
||||
|
||||
func handleVideo(ctx *zero.Ctx) {
|
||||
id := ctx.State["regex_matched"].([]string)[1]
|
||||
if id == "" {
|
||||
id = ctx.State["regex_matched"].([]string)[2]
|
||||
}
|
||||
card, err := getVideoInfo(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
msg, err := videoCard2msg(card)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(msg...)
|
||||
}
|
||||
|
||||
func handleDynamic(ctx *zero.Ctx) {
|
||||
msg, err := dynamicDetail(ctx.State["regex_matched"].([]string)[2])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(msg...)
|
||||
}
|
||||
|
||||
func handleArticle(ctx *zero.Ctx) {
|
||||
card, err := getArticleInfo(ctx.State["regex_matched"].([]string)[1])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(articleCard2msg(card, ctx.State["regex_matched"].([]string)[1])...)
|
||||
}
|
||||
|
||||
func handleLive(ctx *zero.Ctx) {
|
||||
card, err := getLiveRoomInfo(ctx.State["regex_matched"].([]string)[1])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(liveCard2msg(card)...)
|
||||
}
|
||||
@@ -41,8 +41,8 @@ func (config) TableName() string {
|
||||
return "config"
|
||||
}
|
||||
|
||||
// initialize 初始化vtb数据库
|
||||
func initialize(dbpath string) (*vupdb, error) {
|
||||
// initializeVup 初始化vup数据库
|
||||
func initializeVup(dbpath string) (*vupdb, error) {
|
||||
if _, err := os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
357
plugin/bilibili/bilibilipush.go
Normal file
357
plugin/bilibili/bilibilipush.go
Normal file
@@ -0,0 +1,357 @@
|
||||
// Package bilibili b站推送
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"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/img/text"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
)
|
||||
|
||||
const (
|
||||
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36"
|
||||
referer = "https://www.bilibili.com/"
|
||||
infoURL = "https://api.bilibili.com/x/space/acc/info?mid=%v"
|
||||
serviceName = "bilibilipush"
|
||||
)
|
||||
|
||||
// bdb bilibili推送数据库
|
||||
var bdb *bilibilipushdb
|
||||
|
||||
var (
|
||||
lastTime = map[int64]int64{}
|
||||
liveStatus = map[int64]int{}
|
||||
upMap = map[int64]string{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.Register(serviceName, &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "bilibilipush,需要配合job一起使用\n" +
|
||||
"- 添加b站订阅[uid|name]\n" +
|
||||
"- 取消b站订阅[uid|name]\n" +
|
||||
"- 取消b站动态订阅[uid|name]\n" +
|
||||
"- 取消b站直播订阅[uid|name]\n" +
|
||||
"- b站推送列表\n" +
|
||||
"- 拉取b站推送 (使用job执行定时任务------记录在\"@every 10s\"触发的指令)",
|
||||
PrivateDataFolder: serviceName,
|
||||
})
|
||||
|
||||
// 加载bilibili推送数据库
|
||||
dbpath := en.DataFolder()
|
||||
dbfile := dbpath + "push.db"
|
||||
bdb = initializePush(dbfile)
|
||||
|
||||
en.OnRegex(`^添加b站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
name, err := getName(buid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := subscribe(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已添加" + name + "的订阅"))
|
||||
})
|
||||
en.OnRegex(`^取消b站订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
name, err := getName(buid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribe(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已取消" + name + "的订阅"))
|
||||
})
|
||||
en.OnRegex(`^取消b站动态订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
name, err := getName(buid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
if err := unsubscribeDynamic(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已取消" + name + "的动态订阅"))
|
||||
})
|
||||
en.OnRegex(`^取消b站直播订阅\s?(.{1,25})$`, zero.UserOrGrpAdmin, getPara).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
buid, _ := strconv.ParseInt(ctx.State["uid"].(string), 10, 64)
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
name, err := getName(buid)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
if err := unsubscribeLive(buid, gid); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已取消" + name + "的直播订阅"))
|
||||
})
|
||||
en.OnFullMatch("b站推送列表", zero.UserOrGrpAdmin).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
bpl := bdb.getAllPushByGroup(gid)
|
||||
msg := "--------b站推送列表--------"
|
||||
for _, v := range bpl {
|
||||
if _, ok := upMap[v.BilibiliUID]; !ok {
|
||||
bdb.updateAllUp()
|
||||
}
|
||||
msg += fmt.Sprintf("\nuid:%-12d 动态:", v.BilibiliUID)
|
||||
if v.DynamicDisable == 0 {
|
||||
msg += "●"
|
||||
} else {
|
||||
msg += "○"
|
||||
}
|
||||
msg += " 直播:"
|
||||
if v.LiveDisable == 0 {
|
||||
msg += "●"
|
||||
} else {
|
||||
msg += "○"
|
||||
}
|
||||
msg += " up主:" + upMap[v.BilibiliUID]
|
||||
}
|
||||
data, err := text.RenderToBase64(msg, text.FontFile, 600, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控了"))
|
||||
}
|
||||
})
|
||||
en.OnFullMatch("拉取b站推送").SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
err := sendDynamic(ctx)
|
||||
if err != nil {
|
||||
ctx.SendPrivateMessage(ctx.Event.UserID, message.Text("Error: bilibilipush,", err))
|
||||
}
|
||||
err = sendLive(ctx)
|
||||
if err != nil {
|
||||
ctx.SendPrivateMessage(ctx.Event.UserID, message.Text("Error: bilibilipush,", err))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 取得uid的名字
|
||||
func getName(buid int64) (name string, err error) {
|
||||
var ok bool
|
||||
if name, ok = upMap[buid]; !ok {
|
||||
var data []byte
|
||||
data, err = web.RequestDataWith(web.NewDefaultClient(), fmt.Sprintf(infoURL, buid), "GET", referer, ua)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
status := int(gjson.Get(binary.BytesToString(data), "code").Int())
|
||||
if status != 0 {
|
||||
err = errors.New(gjson.Get(binary.BytesToString(data), "message").String())
|
||||
return
|
||||
}
|
||||
name = gjson.Get(binary.BytesToString(data), "data.name").String()
|
||||
bdb.insertBilibiliUp(buid, name)
|
||||
upMap[buid] = name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// subscribe 订阅
|
||||
func subscribe(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 0,
|
||||
"dynamic_disable": 0,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
// unsubscribe 取消订阅
|
||||
func unsubscribe(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 1,
|
||||
"dynamic_disable": 1,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
func unsubscribeDynamic(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"dynamic_disable": 1,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
func unsubscribeLive(buid, groupid int64) (err error) {
|
||||
bpMap := map[string]interface{}{
|
||||
"bilibili_uid": buid,
|
||||
"group_id": groupid,
|
||||
"live_disable": 1,
|
||||
}
|
||||
return bdb.insertOrUpdateLiveAndDynamic(bpMap)
|
||||
}
|
||||
|
||||
func getUserDynamicCard(buid int64) (cardList []gjson.Result, err error) {
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), fmt.Sprintf(spaceHistoryURL, buid, 0), "GET", referer, ua)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cardList = gjson.Get(binary.BytesToString(data), "data.cards").Array()
|
||||
return
|
||||
}
|
||||
|
||||
func getLiveList(uids ...int64) (string, error) {
|
||||
m := make(map[string]interface{})
|
||||
m["uids"] = uids
|
||||
b, err := json.Marshal(m)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
data, err := web.PostData(liveListURL, "application/json", bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return binary.BytesToString(data), nil
|
||||
}
|
||||
|
||||
func sendDynamic(ctx *zero.Ctx) error {
|
||||
uids := bdb.getAllBuidByDynamic()
|
||||
for _, buid := range uids {
|
||||
time.Sleep(2 * time.Second)
|
||||
cardList, err := getUserDynamicCard(buid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cardList) == 0 {
|
||||
return errors.Errorf("%v的历史动态数为0", buid)
|
||||
}
|
||||
t, ok := lastTime[buid]
|
||||
// 第一次先记录时间,啥也不做
|
||||
if !ok {
|
||||
lastTime[buid] = cardList[0].Get("desc.timestamp").Int()
|
||||
return nil
|
||||
}
|
||||
for i := len(cardList) - 1; i >= 0; i-- {
|
||||
ct := cardList[i].Get("desc.timestamp").Int()
|
||||
if ct > t && ct > time.Now().Unix()-600 {
|
||||
lastTime[buid] = ct
|
||||
m, ok := control.Lookup(serviceName)
|
||||
if ok {
|
||||
groupList := bdb.getAllGroupByBuidAndDynamic(buid)
|
||||
msg, err := dynamicCard2msg(cardList[i].Raw, 0)
|
||||
if err != nil {
|
||||
err = errors.Errorf("动态%v的解析有问题,%v", cardList[i].Get("desc.dynamic_id_str"), err)
|
||||
return err
|
||||
}
|
||||
for _, gid := range groupList {
|
||||
if m.IsEnabledIn(gid) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
switch {
|
||||
case gid > 0:
|
||||
ctx.SendGroupMessage(gid, msg)
|
||||
case gid < 0:
|
||||
ctx.SendPrivateMessage(-gid, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func sendLive(ctx *zero.Ctx) error {
|
||||
uids := bdb.getAllBuidByLive()
|
||||
ll, err := getLiveList(uids...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
gjson.Get(ll, "data").ForEach(func(key, value gjson.Result) bool {
|
||||
newStatus := int(value.Get("live_status").Int())
|
||||
if newStatus == 2 {
|
||||
newStatus = 0
|
||||
}
|
||||
if _, ok := liveStatus[key.Int()]; !ok {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
return true
|
||||
}
|
||||
oldStatus := liveStatus[key.Int()]
|
||||
if newStatus != oldStatus && newStatus == 1 {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
m, ok := control.Lookup(serviceName)
|
||||
if ok {
|
||||
groupList := bdb.getAllGroupByBuidAndLive(key.Int())
|
||||
roomID := value.Get("short_id").Int()
|
||||
if roomID == 0 {
|
||||
roomID = value.Get("room_id").Int()
|
||||
}
|
||||
lURL := liveURL + strconv.FormatInt(roomID, 10)
|
||||
lName := value.Get("uname").String()
|
||||
lTitle := value.Get("title").String()
|
||||
lCover := value.Get("cover_from_user").String()
|
||||
if lCover == "" {
|
||||
lCover = value.Get("keyframe").String()
|
||||
}
|
||||
var msg []message.MessageSegment
|
||||
msg = append(msg, message.Text(lName+" 正在直播:\n"))
|
||||
msg = append(msg, message.Text(lTitle))
|
||||
msg = append(msg, message.Image(lCover))
|
||||
msg = append(msg, message.Text("直播链接:", lURL))
|
||||
for _, gid := range groupList {
|
||||
if m.IsEnabledIn(gid) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
switch {
|
||||
case gid > 0:
|
||||
ctx.SendGroupMessage(gid, msg)
|
||||
case gid < 0:
|
||||
ctx.SendPrivateMessage(-gid, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if newStatus != oldStatus {
|
||||
liveStatus[key.Int()] = newStatus
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
}
|
||||
150
plugin/bilibili/bilibilipushmodel.go
Normal file
150
plugin/bilibili/bilibilipushmodel.go
Normal file
@@ -0,0 +1,150 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
_ "github.com/fumiama/sqlite3" // import sql
|
||||
"github.com/jinzhu/gorm"
|
||||
)
|
||||
|
||||
// bilibilipushdb bilibili推送数据库
|
||||
type bilibilipushdb gorm.DB
|
||||
|
||||
type bilibilipush struct {
|
||||
ID int64 `gorm:"column:id;primary_key" json:"id"`
|
||||
BilibiliUID int64 `gorm:"column:bilibili_uid;index:idx_buid_gid" json:"bilibili_uid"`
|
||||
GroupID int64 `gorm:"column:group_id;index:idx_buid_gid" json:"group_id"`
|
||||
LiveDisable int64 `gorm:"column:live_disable;default:0" json:"live_disable"`
|
||||
DynamicDisable int64 `gorm:"column:dynamic_disable;default:0" json:"dynamic_disable"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (bilibilipush) TableName() string {
|
||||
return "bilibili_push"
|
||||
}
|
||||
|
||||
type bilibiliup struct {
|
||||
BilibiliUID int64 `gorm:"column:bilibili_uid;primary_key"`
|
||||
Name string `gorm:"column:name"`
|
||||
}
|
||||
|
||||
// TableName ...
|
||||
func (bilibiliup) TableName() string {
|
||||
return "bilibili_up"
|
||||
}
|
||||
|
||||
// initializePush 初始化bilibilipushdb数据库
|
||||
func initializePush(dbpath string) *bilibilipushdb {
|
||||
var err error
|
||||
if _, err = os.Stat(dbpath); err != nil || os.IsNotExist(err) {
|
||||
// 生成文件
|
||||
f, err := os.Create(dbpath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
}
|
||||
gdb, err := gorm.Open("sqlite3", dbpath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(&bilibilipush{}).AutoMigrate(&bilibiliup{})
|
||||
return (*bilibilipushdb)(gdb)
|
||||
}
|
||||
|
||||
// insertOrUpdateLiveAndDynamic 插入或更新数据库
|
||||
func (bdb *bilibilipushdb) insertOrUpdateLiveAndDynamic(bpMap map[string]interface{}) (err error) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
bp := bilibilipush{}
|
||||
data, err := json.Marshal(&bpMap)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &bp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err = db.Model(&bilibilipush{}).First(&bp, "bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Error; err != nil {
|
||||
if gorm.IsRecordNotFoundError(err) {
|
||||
err = db.Model(&bilibilipush{}).Create(&bp).Error
|
||||
}
|
||||
} else {
|
||||
err = db.Model(&bilibilipush{}).Where("bilibili_uid = ? and group_id = ?", bp.BilibiliUID, bp.GroupID).Update(bpMap).Error
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) getAllBuidByLive() (buidList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "live_disable = 0")
|
||||
temp := make(map[int64]bool)
|
||||
for _, v := range bpl {
|
||||
_, ok := temp[v.BilibiliUID]
|
||||
if !ok {
|
||||
buidList = append(buidList, v.BilibiliUID)
|
||||
temp[v.BilibiliUID] = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) getAllBuidByDynamic() (buidList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "dynamic_disable = 0")
|
||||
temp := make(map[int64]bool)
|
||||
for _, v := range bpl {
|
||||
_, ok := temp[v.BilibiliUID]
|
||||
if !ok {
|
||||
buidList = append(buidList, v.BilibiliUID)
|
||||
temp[v.BilibiliUID] = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) getAllGroupByBuidAndLive(buid int64) (groupList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and live_disable = 0", buid)
|
||||
for _, v := range bpl {
|
||||
groupList = append(groupList, v.GroupID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) getAllGroupByBuidAndDynamic(buid int64) (groupList []int64) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bpl []bilibilipush
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "bilibili_uid = ? and dynamic_disable = 0", buid)
|
||||
for _, v := range bpl {
|
||||
groupList = append(groupList, v.GroupID)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) getAllPushByGroup(groupID int64) (bpl []bilibilipush) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
db.Model(&bilibilipush{}).Find(&bpl, "group_id = ? and (live_disable = 0 or dynamic_disable = 0)", groupID)
|
||||
return
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) insertBilibiliUp(buid int64, name string) {
|
||||
db := (*gorm.DB)(bdb)
|
||||
bu := bilibiliup{
|
||||
BilibiliUID: buid,
|
||||
Name: name,
|
||||
}
|
||||
db.Model(&bilibiliup{}).Create(bu)
|
||||
}
|
||||
|
||||
func (bdb *bilibilipushdb) updateAllUp() {
|
||||
db := (*gorm.DB)(bdb)
|
||||
var bul []bilibiliup
|
||||
db.Model(&bilibiliup{}).Find(&bul)
|
||||
for _, v := range bul {
|
||||
upMap[v.BilibiliUID] = v.Name
|
||||
}
|
||||
}
|
||||
233
plugin/bilibili/card2msg.go
Normal file
233
plugin/bilibili/card2msg.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/zbputils/binary"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
"github.com/tidwall/gjson"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
typeMsg = map[int]string{
|
||||
1: "转发了动态",
|
||||
2: "有图营业",
|
||||
4: "无图营业",
|
||||
8: "投稿了视频",
|
||||
16: "投稿了短视频",
|
||||
64: "投稿了文章",
|
||||
256: "投稿了音频",
|
||||
2048: "发布了简报",
|
||||
4200: "发布了直播",
|
||||
4308: "发布了直播",
|
||||
}
|
||||
)
|
||||
|
||||
// dynamicCard2msg cType=0时,处理DynCard字符串,cType=1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308时,处理Card字符串,cType为card类型
|
||||
func dynamicCard2msg(str string, cType int) (msg []message.MessageSegment, err error) {
|
||||
var (
|
||||
dynamicCard dynamicCard
|
||||
card Card
|
||||
vote Vote
|
||||
)
|
||||
msg = make([]message.MessageSegment, 0, 16)
|
||||
// 初始化结构体
|
||||
switch cType {
|
||||
case 0:
|
||||
err = json.Unmarshal(binary.StringToBytes(str), &dynamicCard)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(binary.StringToBytes(dynamicCard.Card), &card)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if dynamicCard.Extension.Vote != "" {
|
||||
err = json.Unmarshal(binary.StringToBytes(dynamicCard.Extension.Vote), &vote)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
cType = dynamicCard.Desc.Type
|
||||
case 1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308:
|
||||
err = json.Unmarshal(binary.StringToBytes(str), &card)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = errors.New("只有0, 1, 2, 4, 8, 16, 64, 256, 2048, 4200, 4308模式")
|
||||
return
|
||||
}
|
||||
// 生成消息
|
||||
switch cType {
|
||||
case 1:
|
||||
msg = append(msg, message.Text(card.User.Uname, typeMsg[cType], "\n",
|
||||
card.Item.Content, "\n",
|
||||
"转发的内容: \n"))
|
||||
var originMsg []message.MessageSegment
|
||||
originMsg, err = dynamicCard2msg(card.Origin, card.Item.OrigType)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msg = append(msg, originMsg...)
|
||||
case 2:
|
||||
msg = append(msg, message.Text(card.User.Name, "在", time.Unix(int64(card.Item.UploadTime), 0).Format("2006-01-02 15:04:05"), typeMsg[cType], "\n",
|
||||
card.Item.Description))
|
||||
for i := 0; i < len(card.Item.Pictures); i++ {
|
||||
msg = append(msg, message.Image(card.Item.Pictures[i].ImgSrc))
|
||||
}
|
||||
case 4:
|
||||
msg = append(msg, message.Text(card.User.Uname, "在", time.Unix(int64(card.Item.Timestamp), 0).Format("2006-01-02 15:04:05"), typeMsg[cType], "\n",
|
||||
card.Item.Content, "\n"))
|
||||
if dynamicCard.Extension.Vote != "" {
|
||||
msg = append(msg, message.Text("【投票】", vote.Desc, "\n",
|
||||
"截止日期: ", time.Unix(int64(vote.Endtime), 0).Format("2006-01-02 15:04:05"), "\n",
|
||||
"参与人数: ", humanNum(vote.JoinNum), "\n",
|
||||
"投票选项( 最多选择", vote.ChoiceCnt, "项 )\n"))
|
||||
for i := 0; i < len(vote.Options); i++ {
|
||||
msg = append(msg, message.Text("- ", vote.Options[i].Idx, ". ", vote.Options[i].Desc, "\n"))
|
||||
if vote.Options[i].ImgURL != "" {
|
||||
msg = append(msg, message.Image(vote.Options[i].ImgURL))
|
||||
}
|
||||
}
|
||||
}
|
||||
case 8:
|
||||
msg = append(msg, message.Text(card.Owner.Name, "在", time.Unix(int64(card.Pubdate), 0).Format("2006-01-02 15:04:05"), typeMsg[cType], "\n",
|
||||
card.Title))
|
||||
msg = append(msg, message.Image(card.Pic))
|
||||
msg = append(msg, message.Text(card.Desc, "\n",
|
||||
card.ShareSubtitle, "\n",
|
||||
"视频链接: ", card.ShortLink, "\n"))
|
||||
case 16:
|
||||
msg = append(msg, message.Text(card.User.Name, "在", time.Unix(int64(card.Item.UploadTime), 0).Format("2006-01-02 15:04:05"), typeMsg[cType], "\n",
|
||||
card.Item.Description))
|
||||
msg = append(msg, message.Image(card.Item.Cover.Default))
|
||||
case 64:
|
||||
msg = append(msg, message.Text(card.Author.(map[string]interface{})["name"], "在", time.Unix(int64(card.PublishTime), 0).Format("2006-01-02 15:04:05"), typeMsg[cType], "\n",
|
||||
card.Title, "\n",
|
||||
card.Summary))
|
||||
for i := 0; i < len(card.ImageUrls); i++ {
|
||||
msg = append(msg, message.Image(card.ImageUrls[i]))
|
||||
}
|
||||
if card.ID != 0 {
|
||||
msg = append(msg, message.Text("文章链接: https://www.bilibili.com/read/cv", card.ID, "\n"))
|
||||
}
|
||||
case 256:
|
||||
msg = append(msg, message.Text(card.Upper, "在", time.Unix(int64(card.Ctime), 0).Format("2006-01-02 15:04:05"), typeMsg[cType], "\n",
|
||||
card.Title))
|
||||
msg = append(msg, message.Image(card.Cover))
|
||||
msg = append(msg, message.Text(card.Intro, "\n"))
|
||||
if card.ID != 0 {
|
||||
msg = append(msg, message.Text("音频链接: https://www.bilibili.com/audio/au", card.ID, "\n"))
|
||||
}
|
||||
|
||||
case 2048:
|
||||
msg = append(msg, message.Text(card.User.Uname, typeMsg[cType], "\n",
|
||||
card.Vest.Content, "\n",
|
||||
card.Sketch.Title, "\n",
|
||||
card.Sketch.DescText, "\n"))
|
||||
msg = append(msg, message.Image(card.Sketch.CoverURL))
|
||||
msg = append(msg, message.Text("分享链接: ", card.Sketch.TargetURL, "\n"))
|
||||
case 4308:
|
||||
if dynamicCard.Desc.UserProfile.Info.Uname != "" {
|
||||
msg = append(msg, message.Text(dynamicCard.Desc.UserProfile.Info.Uname, typeMsg[cType], "\n"))
|
||||
}
|
||||
msg = append(msg, message.Image(card.LivePlayInfo.Cover))
|
||||
msg = append(msg, message.Text("\n", card.LivePlayInfo.Title, "\n",
|
||||
"房间号: ", card.LivePlayInfo.RoomID, "\n",
|
||||
"分区: ", card.LivePlayInfo.ParentAreaName))
|
||||
if card.LivePlayInfo.ParentAreaName != card.LivePlayInfo.AreaName {
|
||||
msg = append(msg, message.Text("-", card.LivePlayInfo.AreaName))
|
||||
}
|
||||
if card.LivePlayInfo.LiveStatus == 0 {
|
||||
msg = append(msg, message.Text("未开播 \n"))
|
||||
} else {
|
||||
msg = append(msg, message.Text("直播中 ", card.LivePlayInfo.WatchedShow, "\n"))
|
||||
}
|
||||
msg = append(msg, message.Text("直播链接: ", card.LivePlayInfo.Link))
|
||||
default:
|
||||
msg = append(msg, message.Text("动态id: ", dynamicCard.Desc.DynamicIDStr, "未知动态类型: ", cType, "\n"))
|
||||
}
|
||||
if dynamicCard.Desc.DynamicIDStr != "" {
|
||||
msg = append(msg, message.Text("动态链接: ", tURL, dynamicCard.Desc.DynamicIDStr))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// dynamicDetail 用动态id查动态信息
|
||||
func dynamicDetail(dynamicIDStr string) (msg []message.MessageSegment, err error) {
|
||||
var data []byte
|
||||
data, err = web.GetData(fmt.Sprintf(dynamicDetailURL, dynamicIDStr))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return dynamicCard2msg(gjson.ParseBytes(data).Get("data.card").Raw, 0)
|
||||
}
|
||||
|
||||
// articleCard2msg 专栏转消息
|
||||
func articleCard2msg(card Card, defaultID string) (msg []message.MessageSegment) {
|
||||
msg = make([]message.MessageSegment, 0, 16)
|
||||
for i := 0; i < len(card.OriginImageUrls); i++ {
|
||||
msg = append(msg, message.Image(card.OriginImageUrls[i]))
|
||||
}
|
||||
msg = append(msg, message.Text("\n", card.Title, "\n", "UP主: ", card.AuthorName, "\n",
|
||||
"阅读: ", humanNum(card.Stats.View), " 评论: ", humanNum(card.Stats.Reply), "\n",
|
||||
cvURL, defaultID))
|
||||
return
|
||||
}
|
||||
|
||||
// liveCard2msg 直播卡片转消息
|
||||
func liveCard2msg(card roomCard) (msg []message.MessageSegment) {
|
||||
msg = make([]message.MessageSegment, 0, 16)
|
||||
msg = append(msg, message.Image(card.RoomInfo.Keyframe))
|
||||
msg = append(msg, message.Text("\n", card.RoomInfo.Title, "\n",
|
||||
"主播: ", card.AnchorInfo.BaseInfo.Uname, "\n",
|
||||
"房间号: ", card.RoomInfo.RoomID, "\n"))
|
||||
if card.RoomInfo.ShortID != 0 {
|
||||
msg = append(msg, message.Text("短号: ", card.RoomInfo.ShortID, "\n"))
|
||||
}
|
||||
msg = append(msg, message.Text("分区: ", card.RoomInfo.ParentAreaName))
|
||||
if card.RoomInfo.ParentAreaName != card.RoomInfo.AreaName {
|
||||
msg = append(msg, message.Text("-", card.RoomInfo.AreaName))
|
||||
}
|
||||
if card.RoomInfo.LiveStatus == 0 {
|
||||
msg = append(msg, message.Text("未开播 \n"))
|
||||
} else {
|
||||
msg = append(msg, message.Text("直播中 ", humanNum(card.RoomInfo.Online), "人气\n"))
|
||||
}
|
||||
if card.RoomInfo.ShortID != 0 {
|
||||
msg = append(msg, message.Text("直播间链接: ", lURL, card.RoomInfo.ShortID))
|
||||
} else {
|
||||
msg = append(msg, message.Text("直播间链接: ", lURL, card.RoomInfo.RoomID))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// videoCard2msg 视频卡片转消息
|
||||
func videoCard2msg(card Card) (msg []message.MessageSegment, err error) {
|
||||
var mCard memberCard
|
||||
msg = make([]message.MessageSegment, 0, 16)
|
||||
mCard, err = getMemberCard(card.Owner.Mid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
msg = append(msg, message.Text("标题: ", card.Title, "\n"))
|
||||
if card.Rights.IsCooperation == 1 {
|
||||
for i := 0; i < len(card.Staff); i++ {
|
||||
msg = append(msg, message.Text(card.Staff[i].Title, ": ", card.Staff[i].Name, " 粉丝: ", humanNum(card.Staff[i].Follower), "\n"))
|
||||
}
|
||||
} else {
|
||||
msg = append(msg, message.Text("UP主: ", card.Owner.Name, " 粉丝: ", humanNum(mCard.Fans), "\n"))
|
||||
}
|
||||
msg = append(msg, message.Text("播放: ", humanNum(card.Stat.View), " 弹幕: ", humanNum(card.Stat.Danmaku)))
|
||||
msg = append(msg, message.Image(card.Pic))
|
||||
msg = append(msg, message.Text("\n点赞: ", humanNum(card.Stat.Like), " 投币: ", humanNum(card.Stat.Coin), "\n",
|
||||
"收藏: ", humanNum(card.Stat.Favorite), " 分享: ", humanNum(card.Stat.Share), "\n",
|
||||
vURL, card.BvID))
|
||||
return
|
||||
}
|
||||
82
plugin/bilibili/card2msg_test.go
Normal file
82
plugin/bilibili/card2msg_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestArticleInfo(t *testing.T) {
|
||||
card, err := getArticleInfo("17279244")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(articleCard2msg(card, "17279244"))
|
||||
|
||||
}
|
||||
|
||||
func TestDynamicDetail(t *testing.T) {
|
||||
t.Log("cType = 1")
|
||||
t.Log(dynamicDetail("642279068898689029"))
|
||||
|
||||
t.Log("cType = 2")
|
||||
t.Log(dynamicDetail("642470680290394121"))
|
||||
|
||||
t.Log("cType = 2048")
|
||||
t.Log(dynamicDetail("642277677329285174"))
|
||||
|
||||
t.Log("cType = 4")
|
||||
t.Log(dynamicDetail("642154347357011968"))
|
||||
|
||||
t.Log("cType = 8")
|
||||
t.Log(dynamicDetail("675892999274627104"))
|
||||
|
||||
t.Log("cType = 4308")
|
||||
t.Log(dynamicDetail("668598718656675844"))
|
||||
|
||||
t.Log("cType = 64")
|
||||
t.Log(dynamicDetail("675966082178088963"))
|
||||
|
||||
t.Log("cType = 256")
|
||||
t.Log(dynamicDetail("599253048535707632"))
|
||||
|
||||
t.Log("cType = 4,投票类型")
|
||||
t.Log(dynamicDetail("677231070435868704"))
|
||||
}
|
||||
|
||||
func TestMemberCard(t *testing.T) {
|
||||
card, err := getMemberCard(2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("%+v\n", card)
|
||||
}
|
||||
|
||||
func TestVideoInfo(t *testing.T) {
|
||||
card, err := getVideoInfo("10007")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(videoCard2msg(card))
|
||||
card, err = getVideoInfo("BV1xx411c7mD")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(videoCard2msg(card))
|
||||
card, err = getVideoInfo("bv1xx411c7mD")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(videoCard2msg(card))
|
||||
card, err = getVideoInfo("BV1mF411j7iU")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(videoCard2msg(card))
|
||||
}
|
||||
|
||||
func TestLiveRoomInfo(t *testing.T) {
|
||||
card, err := getLiveRoomInfo("83171")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(liveCard2msg(card))
|
||||
}
|
||||
273
plugin/bilibili/types.go
Normal file
273
plugin/bilibili/types.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package bilibili
|
||||
|
||||
const (
|
||||
// tURL bilibili动态前缀
|
||||
tURL = "https://t.bilibili.com/"
|
||||
// liveURL bilibili直播前缀
|
||||
liveURL = "https://live.bilibili.com/"
|
||||
// dynamicDetailURL 当前动态信息,一个card
|
||||
dynamicDetailURL = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=%v"
|
||||
// memberCardURL 个人信息
|
||||
memberCardURL = "https://account.bilibili.com/api/member/getCardByMid?mid=%v"
|
||||
// articleInfoURL 查看专栏信息
|
||||
articleInfoURL = "https://api.bilibili.com/x/article/viewinfo?id=%v"
|
||||
// cvURL b站专栏前缀
|
||||
cvURL = "https://www.bilibili.com/read/cv"
|
||||
// liveRoomInfoURL 查看直播间信息
|
||||
liveRoomInfoURL = "https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom?room_id=%v"
|
||||
// lURL b站直播间前缀
|
||||
lURL = "https://live.bilibili.com/"
|
||||
// videoInfoURL 查看视频信息
|
||||
videoInfoURL = "https://api.bilibili.com/x/web-interface/view?aid=%v&bvid=%v"
|
||||
// vURL 视频网址前缀
|
||||
vURL = "https://www.bilibili.com/video/"
|
||||
// searchUserURL 查找b站用户
|
||||
searchUserURL = "http://api.bilibili.com/x/web-interface/search/type?search_type=bili_user&keyword=%v"
|
||||
// vtbDetailURL 查找vtb信息
|
||||
vtbDetailURL = "https://api.vtbs.moe/v1/detail/%v"
|
||||
// medalwallURL 查找牌子
|
||||
medalwallURL = "https://api.live.bilibili.com/xlive/web-ucenter/user/MedalWall?target_id=%v"
|
||||
// spaceHistoryURL 历史动态信息,一共12个card
|
||||
spaceHistoryURL = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid=%v&offset_dynamic_id=%v&need_top=0"
|
||||
// liveListURL 获得直播状态
|
||||
liveListURL = "https://api.live.bilibili.com/room/v1/Room/get_status_info_by_uids"
|
||||
)
|
||||
|
||||
// dynamicCard 总动态结构体,包括desc,card
|
||||
type dynamicCard struct {
|
||||
Desc Desc `json:"desc"`
|
||||
Card string `json:"card"`
|
||||
Extension struct {
|
||||
VoteCfg struct {
|
||||
VoteID int `json:"vote_id"`
|
||||
Desc string `json:"desc"`
|
||||
JoinNum int `json:"join_num"`
|
||||
} `json:"vote_cfg"`
|
||||
Vote string `json:"vote"`
|
||||
} `json:"extension"`
|
||||
}
|
||||
|
||||
// Card 卡片结构体
|
||||
type Card struct {
|
||||
Item struct {
|
||||
Content string `json:"content"`
|
||||
UploadTime int `json:"upload_time"`
|
||||
Description string `json:"description"`
|
||||
Pictures []struct {
|
||||
ImgSrc string `json:"img_src"`
|
||||
} `json:"pictures"`
|
||||
Timestamp int `json:"timestamp"`
|
||||
Cover struct {
|
||||
Default string `json:"default"`
|
||||
} `json:"cover"`
|
||||
OrigType int `json:"orig_type"`
|
||||
} `json:"item"`
|
||||
AID interface{} `json:"aid"`
|
||||
BvID interface{} `json:"bvid"`
|
||||
Dynamic interface{} `json:"dynamic"`
|
||||
Pic string `json:"pic"`
|
||||
Title string `json:"title"`
|
||||
ID int `json:"id"`
|
||||
Summary string `json:"summary"`
|
||||
ImageUrls []string `json:"image_urls"`
|
||||
OriginImageUrls []string `json:"origin_image_urls"`
|
||||
Sketch struct {
|
||||
Title string `json:"title"`
|
||||
DescText string `json:"desc_text"`
|
||||
CoverURL string `json:"cover_url"`
|
||||
TargetURL string `json:"target_url"`
|
||||
} `json:"sketch"`
|
||||
Stat struct {
|
||||
Aid int `json:"aid"`
|
||||
View int `json:"view"`
|
||||
Danmaku int `json:"danmaku"`
|
||||
Reply int `json:"reply"`
|
||||
Favorite int `json:"favorite"`
|
||||
Coin int `json:"coin"`
|
||||
Share int `json:"share"`
|
||||
Like int `json:"like"`
|
||||
} `json:"stat"`
|
||||
Stats struct {
|
||||
Aid int `json:"aid"`
|
||||
View int `json:"view"`
|
||||
Danmaku int `json:"danmaku"`
|
||||
Reply int `json:"reply"`
|
||||
Favorite int `json:"favorite"`
|
||||
Coin int `json:"coin"`
|
||||
Share int `json:"share"`
|
||||
Like int `json:"like"`
|
||||
} `json:"stats"`
|
||||
Owner struct {
|
||||
Name string `json:"name"`
|
||||
Pubdate int `json:"pubdate"`
|
||||
Mid int `json:"mid"`
|
||||
} `json:"owner"`
|
||||
Cover string `json:"cover"`
|
||||
ShortID interface{} `json:"short_id"`
|
||||
LivePlayInfo struct {
|
||||
ParentAreaName string `json:"parent_area_name"`
|
||||
AreaName string `json:"area_name"`
|
||||
Cover string `json:"cover"`
|
||||
Link string `json:"link"`
|
||||
Online int `json:"online"`
|
||||
RoomID int `json:"room_id"`
|
||||
LiveStatus int `json:"live_status"`
|
||||
WatchedShow string `json:"watched_show"`
|
||||
Title string `json:"title"`
|
||||
} `json:"live_play_info"`
|
||||
Intro string `json:"intro"`
|
||||
Schema string `json:"schema"`
|
||||
Author interface{} `json:"author"`
|
||||
AuthorName string `json:"author_name"`
|
||||
PlayCnt int `json:"play_cnt"`
|
||||
ReplyCnt int `json:"reply_cnt"`
|
||||
TypeInfo string `json:"type_info"`
|
||||
User struct {
|
||||
Name string `json:"name"`
|
||||
Uname string `json:"uname"`
|
||||
} `json:"user"`
|
||||
Desc string `json:"desc"`
|
||||
ShareSubtitle string `json:"share_subtitle"`
|
||||
ShortLink string `json:"short_link"`
|
||||
PublishTime int `json:"publish_time"`
|
||||
BannerURL string `json:"banner_url"`
|
||||
Ctime int `json:"ctime"`
|
||||
Vest struct {
|
||||
Content string `json:"content"`
|
||||
} `json:"vest"`
|
||||
Upper string `json:"upper"`
|
||||
Origin string `json:"origin"`
|
||||
Pubdate int `json:"pubdate"`
|
||||
Rights struct {
|
||||
IsCooperation int `json:"is_cooperation"`
|
||||
} `json:"rights"`
|
||||
Staff []struct {
|
||||
Title string `json:"title"`
|
||||
Name string `json:"name"`
|
||||
Follower int `json:"follower"`
|
||||
} `json:"staff"`
|
||||
}
|
||||
|
||||
// Desc 描述结构体
|
||||
type Desc struct {
|
||||
Type int `json:"type"`
|
||||
DynamicIDStr string `json:"dynamic_id_str"`
|
||||
OrigType int `json:"orig_type"`
|
||||
Timestamp int `json:"timestamp"`
|
||||
Origin struct {
|
||||
DynamicIDStr string `json:"dynamic_id_str"`
|
||||
} `json:"origin"`
|
||||
UserProfile struct {
|
||||
Info struct {
|
||||
Uname string `json:"uname"`
|
||||
} `json:"info"`
|
||||
} `json:"user_profile"`
|
||||
}
|
||||
|
||||
// Vote 投票结构体
|
||||
type Vote struct {
|
||||
ChoiceCnt int `json:"choice_cnt"`
|
||||
Desc string `json:"desc"`
|
||||
Endtime int `json:"endtime"`
|
||||
JoinNum int `json:"join_num"`
|
||||
Options []struct {
|
||||
Idx int `json:"idx"`
|
||||
Desc string `json:"desc"`
|
||||
ImgURL string `json:"img_url"`
|
||||
} `json:"options"`
|
||||
}
|
||||
|
||||
// memberCard 个人信息卡片
|
||||
type memberCard struct {
|
||||
Mid string `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
Sex string `json:"sex"`
|
||||
Face string `json:"face"`
|
||||
Coins float64 `json:"coins"`
|
||||
Regtime int64 `json:"regtime"`
|
||||
Birthday string `json:"birthday"`
|
||||
Sign string `json:"sign"`
|
||||
Attentions []int64 `json:"attentions"`
|
||||
Fans int `json:"fans"`
|
||||
Friend int `json:"friend"`
|
||||
Attention int `json:"attention"`
|
||||
LevelInfo struct {
|
||||
CurrentLevel int `json:"current_level"`
|
||||
} `json:"level_info"`
|
||||
}
|
||||
|
||||
// roomCard 直播间卡片
|
||||
type roomCard struct {
|
||||
RoomInfo struct {
|
||||
RoomID int `json:"room_id"`
|
||||
ShortID int `json:"short_id"`
|
||||
Title string `json:"title"`
|
||||
LiveStatus int `json:"live_status"`
|
||||
AreaName string `json:"area_name"`
|
||||
ParentAreaName string `json:"parent_area_name"`
|
||||
Keyframe string `json:"keyframe"`
|
||||
Online int `json:"online"`
|
||||
} `json:"room_info"`
|
||||
AnchorInfo struct {
|
||||
BaseInfo struct {
|
||||
Uname string `json:"uname"`
|
||||
} `json:"base_info"`
|
||||
} `json:"anchor_info"`
|
||||
}
|
||||
|
||||
// searchResult 查找b站用户结果
|
||||
type searchResult struct {
|
||||
Mid int64 `json:"mid"`
|
||||
Uname string `json:"uname"`
|
||||
Gender int64 `json:"gender"`
|
||||
Usign string `json:"usign"`
|
||||
Level int64 `json:"level"`
|
||||
}
|
||||
|
||||
// medalData 牌子接口返回结构体
|
||||
type medalData struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
List []medal `json:"list"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
// medalInfo b站牌子信息
|
||||
type medalInfo struct {
|
||||
Mid int64 `json:"target_id"`
|
||||
MedalName string `json:"medal_name"`
|
||||
Level int64 `json:"level"`
|
||||
MedalColorStart int64 `json:"medal_color_start"`
|
||||
MedalColorEnd int64 `json:"medal_color_end"`
|
||||
MedalColorBorder int64 `json:"medal_color_border"`
|
||||
}
|
||||
|
||||
type medal struct {
|
||||
Uname string `json:"target_name"`
|
||||
medalInfo `json:"medal_info"`
|
||||
}
|
||||
|
||||
type medalSlice []medal
|
||||
|
||||
func (m medalSlice) Len() int {
|
||||
return len(m)
|
||||
}
|
||||
func (m medalSlice) Swap(i, j int) {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
}
|
||||
func (m medalSlice) Less(i, j int) bool {
|
||||
return m[i].Level > m[j].Level
|
||||
}
|
||||
|
||||
// vtb信息
|
||||
type vtbDetail struct {
|
||||
Mid int `json:"mid"`
|
||||
Uname string `json:"uname"`
|
||||
Video int `json:"video"`
|
||||
Roomid int `json:"roomid"`
|
||||
Rise int `json:"rise"`
|
||||
Follower int `json:"follower"`
|
||||
GuardNum int `json:"guardNum"`
|
||||
AreaRank int `json:"areaRank"`
|
||||
}
|
||||
25
plugin/bilibili/util.go
Normal file
25
plugin/bilibili/util.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package bilibili
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// humanNum 格式化人数
|
||||
func humanNum(res int) string {
|
||||
if res/10000 != 0 {
|
||||
return strconv.FormatFloat(float64(res)/10000, 'f', 2, 64) + "万"
|
||||
}
|
||||
return strconv.Itoa(res)
|
||||
}
|
||||
|
||||
// getrealurl 获取跳转后的链接
|
||||
func getrealurl(url string) (realurl string, err error) {
|
||||
data, err := http.Head(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = data.Body.Close()
|
||||
realurl = data.Request.URL.String()
|
||||
return
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
// Package bilibiliparse b站视频链接解析
|
||||
package bilibiliparse
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
type result struct {
|
||||
Data struct {
|
||||
Bvid string `json:"bvid"`
|
||||
Aid int `json:"aid"`
|
||||
Copyright int `json:"copyright"`
|
||||
Pic string `json:"pic"`
|
||||
Title string `json:"title"`
|
||||
Pubdate int `json:"pubdate"`
|
||||
Ctime int `json:"ctime"`
|
||||
Rights struct {
|
||||
IsCooperation int `json:"is_cooperation"`
|
||||
} `json:"rights"`
|
||||
Owner struct {
|
||||
Mid int `json:"mid"`
|
||||
Name string `json:"name"`
|
||||
} `json:"owner"`
|
||||
Stat struct {
|
||||
Aid int `json:"aid"`
|
||||
View int `json:"view"`
|
||||
Danmaku int `json:"danmaku"`
|
||||
Reply int `json:"reply"`
|
||||
Favorite int `json:"favorite"`
|
||||
Coin int `json:"coin"`
|
||||
Share int `json:"share"`
|
||||
Like int `json:"like"`
|
||||
} `json:"stat"`
|
||||
Staff []struct {
|
||||
Title string `json:"title"`
|
||||
Name string `json:"name"`
|
||||
Follower int `json:"follower"`
|
||||
} `json:"staff"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type owner struct {
|
||||
Data struct {
|
||||
Card struct {
|
||||
Fans int `json:"fans"`
|
||||
} `json:"card"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
const (
|
||||
videoapi = "https://api.bilibili.com/x/web-interface/view?"
|
||||
cardapi = "http://api.bilibili.com/x/web-interface/card?"
|
||||
origin = "https://www.bilibili.com/video/"
|
||||
)
|
||||
|
||||
var (
|
||||
reg = regexp.MustCompile(`https://www.bilibili.com/video/([0-9a-zA-Z]+)`)
|
||||
limit = ctxext.NewLimiterManager(time.Second*10, 1)
|
||||
)
|
||||
|
||||
// 插件主体
|
||||
func init() {
|
||||
en := control.Register("bilibiliparse", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "b站视频链接解析\n" +
|
||||
"- https://www.bilibili.com/video/BV1xx411c7BF | https://www.bilibili.com/video/av1605 | https://b23.tv/I8uzWCA | https://www.bilibili.com/video/bv1xx411c7BF",
|
||||
})
|
||||
en.OnRegex(`(av[0-9]+|BV[0-9a-zA-Z]{10}){1}`).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
if strings.Contains(ctx.MessageString(), "[CQ:forward") {
|
||||
return
|
||||
}
|
||||
id := ctx.State["regex_matched"].([]string)[1]
|
||||
m, err := parse(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(m)
|
||||
})
|
||||
en.OnRegex(`https://www.bilibili.com/video/([0-9a-zA-Z]+)`).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
id := ctx.State["regex_matched"].([]string)[1]
|
||||
m, err := parse(id)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(m)
|
||||
})
|
||||
en.OnRegex(`(https://b23.tv/[0-9a-zA-Z]+)`).SetBlock(true).Limit(limit.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
url := ctx.State["regex_matched"].([]string)[1]
|
||||
realurl, err := getrealurl(url)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
m, err := parse(cuturl(realurl))
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.Send(m)
|
||||
})
|
||||
}
|
||||
|
||||
// parse 解析视频数据
|
||||
func parse(id string) (m message.Message, err error) {
|
||||
var vid string
|
||||
switch id[:2] {
|
||||
case "av":
|
||||
vid = "aid=" + id[2:]
|
||||
case "BV":
|
||||
vid = "bvid=" + id
|
||||
}
|
||||
data, err := web.GetData(videoapi + vid)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var r result
|
||||
err = json.Unmarshal(data, &r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
m = make(message.Message, 0, 16)
|
||||
m = append(m, message.Text("标题: ", r.Data.Title, "\n"))
|
||||
if r.Data.Rights.IsCooperation == 1 {
|
||||
for i := 0; i < len(r.Data.Staff); i++ {
|
||||
m = append(m, message.Text(r.Data.Staff[i].Title, ": ", r.Data.Staff[i].Name, ", 粉丝: ", row(r.Data.Staff[i].Follower), "\n"))
|
||||
}
|
||||
} else {
|
||||
o, err := getcard(r.Data.Owner.Mid)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
m = append(m, message.Text("UP主: ", r.Data.Owner.Name, ", 粉丝: ", row(o.Data.Card.Fans), "\n"))
|
||||
}
|
||||
m = append(m, message.Text("播放: ", row(r.Data.Stat.View), ", 弹幕: ", row(r.Data.Stat.Danmaku), "\n"),
|
||||
message.Image(r.Data.Pic),
|
||||
message.Text("\n点赞: ", row(r.Data.Stat.Like), ", 投币: ", row(r.Data.Stat.Coin), "\n收藏: ", row(r.Data.Stat.Favorite), ", 分享: ", row(r.Data.Stat.Share), "\n", origin, id))
|
||||
return
|
||||
}
|
||||
|
||||
// getrealurl 获取跳转后的链接
|
||||
func getrealurl(url string) (realurl string, err error) {
|
||||
data, err := http.Head(url)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
realurl = data.Request.URL.String()
|
||||
return
|
||||
}
|
||||
|
||||
// cuturl 获取aid或者bvid
|
||||
func cuturl(url string) (id string) {
|
||||
if !reg.MatchString(url) {
|
||||
return
|
||||
}
|
||||
return reg.FindStringSubmatch(url)[1]
|
||||
}
|
||||
|
||||
// getcard 获取个人信息
|
||||
func getcard(mid int) (o owner, err error) {
|
||||
data, err := web.GetData(cardapi + "mid=" + strconv.Itoa(mid))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &o)
|
||||
return
|
||||
}
|
||||
|
||||
func row(res int) string {
|
||||
if res/10000 != 0 {
|
||||
return strconv.FormatFloat(float64(res)/10000, 'f', 2, 64) + "万"
|
||||
}
|
||||
return strconv.Itoa(res)
|
||||
}
|
||||
111
plugin/char_reverser/init.go
Normal file
111
plugin/char_reverser/init.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Package charreverser 英文字符反转
|
||||
package charreverser
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const commandRegex = `[A-z]{1}([A-z]|\s)+[A-z]{1}` // 命令正则表达式
|
||||
|
||||
var (
|
||||
charMap = map[rune]rune{
|
||||
'a': 'ɐ',
|
||||
'b': 'q',
|
||||
'c': 'ɔ',
|
||||
'd': 'p',
|
||||
'e': 'ǝ',
|
||||
'f': 'ɟ',
|
||||
'g': 'ƃ',
|
||||
'h': 'ɥ',
|
||||
'i': 'ᴉ',
|
||||
'j': 'ɾ',
|
||||
'k': 'ʞ',
|
||||
'l': 'l',
|
||||
'm': 'ɯ',
|
||||
'n': 'u',
|
||||
'o': 'o',
|
||||
'p': 'd',
|
||||
'q': 'b',
|
||||
'r': 'ɹ',
|
||||
's': 's',
|
||||
't': 'ʇ',
|
||||
'u': 'n',
|
||||
'v': 'ʌ',
|
||||
'w': 'ʍ',
|
||||
'x': 'x',
|
||||
'y': 'ʎ',
|
||||
'z': 'z',
|
||||
'A': '∀',
|
||||
'B': 'ᗺ',
|
||||
'C': 'Ɔ',
|
||||
'D': 'ᗡ',
|
||||
'E': 'Ǝ',
|
||||
'F': 'Ⅎ',
|
||||
'G': '⅁',
|
||||
'H': 'H',
|
||||
'I': 'I',
|
||||
'J': 'ſ',
|
||||
'K': 'ʞ',
|
||||
'L': '˥',
|
||||
'M': 'W',
|
||||
'N': 'N',
|
||||
'O': 'O',
|
||||
'P': 'Ԁ',
|
||||
'Q': 'Ò',
|
||||
'R': 'ᴚ',
|
||||
'S': 'S',
|
||||
'T': '⏊',
|
||||
'U': '∩',
|
||||
'V': 'Λ',
|
||||
'W': 'M',
|
||||
'X': 'X',
|
||||
'Y': '⅄',
|
||||
'Z': 'Z',
|
||||
}
|
||||
|
||||
compiledRegex = regexp.MustCompile(commandRegex)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 初始化engine
|
||||
engine := control.Register(
|
||||
"charreverser",
|
||||
&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "字符翻转\n -翻转 <英文字符串>",
|
||||
},
|
||||
)
|
||||
// 处理字符翻转指令
|
||||
engine.OnRegex(`翻转( )+[A-z]{1}([A-z]|\s)+[A-z]{1}`).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
// 获取需要翻转的字符串
|
||||
results := compiledRegex.FindAllString(ctx.MessageString(), -1)
|
||||
str := results[0]
|
||||
|
||||
// 将字符顺序翻转
|
||||
var tempBuilder strings.Builder
|
||||
for i := len(str) - 1; i >= 0; i-- {
|
||||
tempBuilder.WriteByte(str[i])
|
||||
}
|
||||
|
||||
// 翻转字符字形
|
||||
var reversedStrBuilder strings.Builder
|
||||
for _, char := range tempBuilder.String() {
|
||||
if char != ' ' {
|
||||
reversedStrBuilder.WriteRune(charMap[char])
|
||||
} else {
|
||||
reversedStrBuilder.WriteRune(' ')
|
||||
}
|
||||
}
|
||||
|
||||
// 发送翻转后的字符串
|
||||
ctx.SendChain(message.Text(reversedStrBuilder.String()))
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/fogleman/gg" // 注册了 jpg png gif
|
||||
"github.com/Coloured-glaze/gg" // 注册了 jpg png gif
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ZeroBot-Plugin-Gif
|
||||
[ZeroBot QQ机器人](https://github.com/wdvxdr1123/ZeroBot)插件,可以制作各种沙雕gif图
|
||||
> 素材包地址: https://gitcode.net/u011570312/imagematerials
|
||||
> 素材包地址: https://gitcode.net/anto_july/imagematerials
|
||||
|
||||
## 触发方式
|
||||
1. [指令词]+[qq号] 如:爬123456
|
||||
@@ -29,3 +29,75 @@
|
||||
- [x] 浮雕
|
||||
- [x] 打码
|
||||
- [x] 负片
|
||||
- [x] 旋转45
|
||||
- [x] 变形100 100
|
||||
- [x] 亲
|
||||
- [x] 娶|结婚申请|结婚登记
|
||||
- [x] 像只
|
||||
- [x] 阿尼亚喜欢
|
||||
- [x] 我永远喜欢|永远喜欢
|
||||
- [x] 像样的亲亲
|
||||
- [x] 国旗
|
||||
- [x] 不要靠近
|
||||
- [x] 万能表情|空白表情
|
||||
- [x] 采访
|
||||
- [x] 需要|你可能需要
|
||||
- [x] 这像画吗
|
||||
- [x] 小画家
|
||||
- [x] 完美
|
||||
- [x] 玩游戏 (应该使用透视变换)
|
||||
- [x] 出警
|
||||
- [x] 警察
|
||||
- [x] 舔|舔屏|prpr (应该使用透视变换)
|
||||
- [x] 安全感
|
||||
- [x] 精神支柱
|
||||
- [x] 想什么
|
||||
- [x] 墙纸
|
||||
- [x] 为什么at我
|
||||
- [x] 交个朋友
|
||||
- [x] 打工人|继续干活
|
||||
- [x] 兑换券
|
||||
- [ ] 捂脸 (使用了透视变换, 需要研究矩阵变换)
|
||||
- [x] 注意力涣散
|
||||
- [x] 垃圾桶|垃圾
|
||||
- [x] 锤
|
||||
- [x] 啾啾
|
||||
- [x] 2敲
|
||||
- [x] 听音乐
|
||||
- [ ] 群青 (需要mask)
|
||||
- [ ] 加载中 (需要mask)
|
||||
- [x] 永远爱你 (未加闪光)
|
||||
- [ ] 关注 (处理文字麻烦)
|
||||
- [x] 2拍
|
||||
- [x] 顶
|
||||
- [x] 捣
|
||||
- [x] 打拳 (未加闪光)
|
||||
- [ ] 复读 (处理文字麻烦)
|
||||
- [x] 滚
|
||||
- [x] 吸
|
||||
- [x] 扔
|
||||
- [x] 捶
|
||||
- [x] 紧贴
|
||||
- [ ] 膜拜 (使用了透视变换, 需要研究矩阵变换)
|
||||
- [ ] 小天使 (摆)
|
||||
- [ ] 一直 (摆)
|
||||
- [x] 转
|
||||
- [ ] 问问 (摆)
|
||||
- [ ] 典中典 (摆)
|
||||
- [ ] 震惊 (摆)
|
||||
- [ ] 哈哈镜 (摆)
|
||||
- [ ] 对称 (猎奇, 不整)
|
||||
- [x] 炖
|
||||
- [x] 2蹭
|
||||
- [x] 诶嘿
|
||||
- [x] 膜拜
|
||||
- [x] 吞
|
||||
- [x] 揍
|
||||
- [x] 给我变
|
||||
- [x] 玩一下
|
||||
- [x] 不要看
|
||||
- [x] 小天使
|
||||
- [x] 你的
|
||||
- [x] 我老婆
|
||||
- [x] 远离
|
||||
- [x] 抬棺
|
||||
|
||||
@@ -20,7 +20,7 @@ func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
|
||||
target := datapath + `materials/` + name
|
||||
var err error
|
||||
if file.IsNotExist(target) {
|
||||
err = file.DownloadTo(`https://gitcode.net/u011570312/imagematerials/-/raw/main/`+name, target, true)
|
||||
err = file.DownloadTo(`https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, target, true)
|
||||
if err != nil {
|
||||
exit(err)
|
||||
return
|
||||
@@ -35,7 +35,7 @@ func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
|
||||
func dlblock(name string) (string, error) {
|
||||
target := datapath + `materials/` + name
|
||||
if file.IsNotExist(target) {
|
||||
err := file.DownloadTo(`https://gitcode.net/u011570312/imagematerials/-/raw/main/`+name, target, true)
|
||||
err := file.DownloadTo(`https://gitcode.net/m0_60838134/imagematerials/-/raw/main/`+name, target, true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -73,8 +73,8 @@ func newContext(user int64) *context {
|
||||
return c
|
||||
}
|
||||
|
||||
func loadFirstFrames(paths []string, size int) (imgs []*img.ImgFactory, err error) {
|
||||
imgs = make([]*img.ImgFactory, size)
|
||||
func loadFirstFrames(paths []string, size int) (imgs []*img.Factory, err error) {
|
||||
imgs = make([]*img.Factory, size)
|
||||
for i := range imgs {
|
||||
imgs[i], err = img.LoadFirstFrame(paths[i], 0, 0)
|
||||
if err != nil {
|
||||
|
||||
1141
plugin/gif/gif.go
1141
plugin/gif/gif.go
File diff suppressed because it is too large
Load Diff
1507
plugin/gif/png.go
1507
plugin/gif/png.go
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
package gif
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -15,19 +14,137 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
cmds = []string{"搓", "冲", "摸", "拍", "丢", "吃", "敲", "啃", "蹭", "爬", "撕",
|
||||
"灰度", "上翻", "下翻", "左翻", "右翻", "反色", "浮雕", "打码", "负片"}
|
||||
cmd = make([]string, 0)
|
||||
datapath string
|
||||
cmdMap = map[string]func(cc *context, args ...string) (string, error){
|
||||
"搓": cuo,
|
||||
"冲": xqe,
|
||||
"摸": mo,
|
||||
"拍": pai,
|
||||
"丢": diu,
|
||||
"吃": chi,
|
||||
"敲": qiao,
|
||||
"啃": ken,
|
||||
"蹭": ceng,
|
||||
"爬": pa,
|
||||
"撕": si,
|
||||
"灰度": grayscale,
|
||||
"上翻": flipV,
|
||||
"下翻": flipV,
|
||||
"左翻": flipH,
|
||||
"右翻": flipH,
|
||||
"反色": invert,
|
||||
"浮雕": convolve3x3,
|
||||
"打码": blur,
|
||||
"负片": invertAndGrayscale,
|
||||
"旋转": rotate,
|
||||
"变形": deformation,
|
||||
"亲": kiss,
|
||||
"结婚申请": marriage,
|
||||
"结婚登记": marriage,
|
||||
"阿尼亚喜欢": anyasuki,
|
||||
"像只": alike,
|
||||
"我永远喜欢": alwaysLike,
|
||||
"永远喜欢": alwaysLike,
|
||||
"像样的亲亲": decentKiss,
|
||||
"国旗": chinaFlag,
|
||||
"不要靠近": dontTouch,
|
||||
"万能表情": universal,
|
||||
"空白表情": universal,
|
||||
"采访": interview,
|
||||
"需要": need,
|
||||
"你可能需要": need,
|
||||
"这像画吗": paint,
|
||||
"小画家": painter,
|
||||
"完美": perfect,
|
||||
"玩游戏": playGame,
|
||||
"出警": police,
|
||||
"警察": police1,
|
||||
"舔": prpr,
|
||||
"舔屏": prpr,
|
||||
"prpr": prpr,
|
||||
"安全感": safeSense,
|
||||
"精神支柱": support,
|
||||
"想什么": thinkwhat,
|
||||
"墙纸": wallpaper,
|
||||
"为什么at我": whyatme,
|
||||
"交个朋友": makeFriend,
|
||||
"打工人": backToWork,
|
||||
"继续干活": backToWork,
|
||||
"兑换券": coupon,
|
||||
"注意力涣散": distracted,
|
||||
"垃圾桶": garbage,
|
||||
"垃圾": garbage,
|
||||
"捶": thump,
|
||||
"啾啾": jiujiu,
|
||||
"2敲": knock,
|
||||
"听音乐": listenMusic,
|
||||
"永远爱你": loveYou,
|
||||
"2拍": pat,
|
||||
"顶": jackUp,
|
||||
"捣": pound,
|
||||
"打拳": punch,
|
||||
"滚": roll,
|
||||
"吸": suck,
|
||||
"嗦": suck,
|
||||
"扔": throw,
|
||||
"锤": hammer,
|
||||
"紧贴": tightly,
|
||||
"紧紧贴着": tightly,
|
||||
"转": turn,
|
||||
"蒙蔽": mengbi,
|
||||
"踩": cai,
|
||||
"好玩": haowan,
|
||||
"2转": whirl,
|
||||
"2滚": push,
|
||||
"踢球": tiqiu,
|
||||
"2舔": lick,
|
||||
"可莉吃": klee,
|
||||
"胡桃啃": hutaoken,
|
||||
"怀": huai,
|
||||
"砰": peng,
|
||||
"你犯法了": fanfa,
|
||||
"炖": dun,
|
||||
"2蹭": ceng2,
|
||||
"诶嘿": eihei,
|
||||
"膜拜": worship,
|
||||
"吞": ci,
|
||||
"揍": zou,
|
||||
"给我变": bian,
|
||||
"玩一下": van,
|
||||
"不要看": neko,
|
||||
"小天使": xiaotianshi,
|
||||
"你的": youer,
|
||||
"我老婆": nowife,
|
||||
"远离": yuanli,
|
||||
"抬棺": taiguan,
|
||||
}
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
for k := range cmdMap {
|
||||
cmd = append(cmd, k)
|
||||
}
|
||||
en := control.Register("gif", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "制图\n- " + strings.Join(cmds, "\n- "),
|
||||
DisableOnDefault: false,
|
||||
Help: "GIF制图,命令后艾特群友/QQ号/一张图方可触发命令\n其中XXX可以为任何文字,可以不写\n对机器人操作请先艾特机器人再执行命令\n" +
|
||||
"- 搓|-冲|-摸|-拍|-丢|-吃|-敲|-啃|-蹭|-爬|-撕|-灰度|-上翻|-下翻\n" +
|
||||
"- 左翻|-右翻|-反色|-浮雕|- 打码|- 负片|- 旋转|- 变形|- 亲\n" +
|
||||
"- 结婚申请|结婚登记|- 阿尼亚喜欢XXX|- 像只|- 我永远喜欢XXX\n" +
|
||||
"- 像样的亲亲|- 国旗|- 不要靠近|- 万能表情|-空白表情|- 采访\n" +
|
||||
"- 需要|-你可能需要|- 这像画吗|- 小画家|- 完美|- 玩游戏|- 出警\n" +
|
||||
"- 警察|- 舔|舔屏|prpr|- 安全感|- 精神支柱|- 想什么|- 墙纸\n" +
|
||||
"- 为什么at我|- 交个朋友|- 打工人|-继续干活|- 兑换券|- 炖\n" +
|
||||
"- 垃圾桶|- 垃圾|- 捶|- 啾啾|- 2敲|- 听音乐|- 永远爱你|- 2拍\n" +
|
||||
"- 顶|- 捣|- 打拳|- 滚|- 吸|- 嗦|- 扔|- 锤|- 紧贴|紧紧贴着|- 转\n" +
|
||||
"- 抬棺|- 远离|- 我老婆|- 小天使XXX|- 你的XXX|- 不要看\n" +
|
||||
"- 玩一下XXX|- 给我变|- 揍|- 吞|- 膜拜|- 诶嘿|- 2蹭|- 你犯法了\n" +
|
||||
"- 砰|- 注意力涣散|- 蒙蔽|- 踩|- 好玩|- 2转|- 踢球|- 2舔|\n" +
|
||||
"- 可莉吃|- 胡桃啃|- 怀",
|
||||
PrivateDataFolder: "gif",
|
||||
}).ApplySingle(ctxext.DefaultSingle)
|
||||
datapath = file.BOTPATH + "/" + en.DataFolder()
|
||||
en.OnRegex(`^(` + strings.Join(cmds, "|") + `)\D*?(\[CQ:(image\,file=([0-9a-zA-Z]{32}).*|at.+?(\d{5,11}))\].*|(\d+))$`).
|
||||
en.OnRegex(`^(` + strings.Join(cmd, "|") + `)[\s\S]*?(\[CQ:(image\,file=([0-9a-zA-Z]{32}).*|at.+?(\d{5,11}))\].*|(\d+))$`).
|
||||
SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
c := newContext(ctx.Event.UserID)
|
||||
list := ctx.State["regex_matched"].([]string)
|
||||
@@ -36,16 +153,8 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
var picurl string
|
||||
if len([]rune(list[1])) == 1 {
|
||||
r := reflect.ValueOf(c).MethodByName("A" + list[1]).Call(nil)
|
||||
picurl = r[0].String()
|
||||
if !r[1].IsNil() {
|
||||
err = r[1].Interface().(error)
|
||||
}
|
||||
} else {
|
||||
picurl, err = c.other(list[1]) // "灰度", "上翻", "下翻", "左翻", "右翻", "反色", "倒放", "浮雕", "打码", "负片"
|
||||
}
|
||||
argslist := strings.Split(strings.TrimSuffix(strings.TrimPrefix(list[0], list[1]), list[2]), " ")
|
||||
picurl, err := cmdMap[list[1]](c, argslist...)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
|
||||
@@ -61,9 +61,9 @@ func init() { // 插件主体
|
||||
"Star/Fork/Issue: ",
|
||||
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
|
||||
"Language: ",
|
||||
notnull(repo.Get("language").Str, "None"), "\n",
|
||||
notnull(repo.Get("language").Str), "\n",
|
||||
"License: ",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str)), "\n",
|
||||
"Last pushed: ",
|
||||
repo.Get("pushed_at").Str, "\n",
|
||||
"Jump: ",
|
||||
@@ -79,9 +79,9 @@ func init() { // 插件主体
|
||||
"Star/Fork/Issue: ",
|
||||
repo.Get("watchers").Int(), "/", repo.Get("forks").Int(), "/", repo.Get("open_issues").Int(), "\n",
|
||||
"Language: ",
|
||||
notnull(repo.Get("language").Str, "None"), "\n",
|
||||
notnull(repo.Get("language").Str), "\n",
|
||||
"License: ",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str), "None"), "\n",
|
||||
notnull(strings.ToUpper(repo.Get("license.key").Str)), "\n",
|
||||
"Last pushed: ",
|
||||
repo.Get("pushed_at").Str, "\n",
|
||||
"Jump: ",
|
||||
@@ -97,9 +97,9 @@ func init() { // 插件主体
|
||||
|
||||
// notnull 如果传入文本为空,则返回默认值
|
||||
|
||||
func notnull(text, defstr string) string {
|
||||
func notnull(text string) string {
|
||||
if text == "" {
|
||||
return defstr
|
||||
return "None"
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/wdvxdr1123/ZeroBot/utils/helper"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
@@ -25,6 +28,10 @@ import (
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/web"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||
|
||||
// 图片输出
|
||||
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,26 +39,32 @@ const (
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
catlist = make(map[string]int64, 100)
|
||||
filelist []string
|
||||
musictypelist = "mp3;MP3;wav;WAV;amr;AMR;3gp;3GP;3gpp;3GPP;acc;ACC"
|
||||
cuttime = [...]string{"00:00:05", "00:00:30", "00:01:00"} // 音乐切割时间点,可自行调节时间(时:分:秒)
|
||||
cfg config
|
||||
)
|
||||
|
||||
func init() { // 插件主体
|
||||
engine := control.Register("guessmusic", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "猜歌插件(该插件依赖ffmpeg)\n" +
|
||||
"- 个人猜歌\n" +
|
||||
"- 团队猜歌\n" +
|
||||
"------bot主人指令------\n" +
|
||||
"- 设置猜歌缓存歌库路径 [绝对路径]\n" +
|
||||
"- 设置猜歌本地 [true/false]\n" +
|
||||
"- 设置猜歌Api [true/false]\n" +
|
||||
"注:默认歌库为网易云热歌榜\n" +
|
||||
"1.可在后面添加“-动漫”进行动漫歌猜歌\n-这个只能猜歌名和歌手\n" +
|
||||
"2.可在后面添加“-动漫2”进行动漫歌猜歌\n-这个可以猜番名,但歌手经常“未知”",
|
||||
"- 设置猜歌[本地/Api] [true/false]\n" +
|
||||
"- 登录网易云\n" +
|
||||
"- 添加歌单 [网易云歌单ID] [歌单名称]\n" +
|
||||
"- 删除歌单 [网易云歌单ID/API歌单名称]\n" +
|
||||
"注:\n1.不登陆也能用,API有几率返回400\n" +
|
||||
"2.[歌单名称]可为空,默认原标题\n" +
|
||||
"------公 用 指 令------\n" +
|
||||
"- 获取歌单列表\n" +
|
||||
"- [网易云歌单ID/API歌单名称]歌单信息\n" +
|
||||
"- [个人/团队]猜歌\n" +
|
||||
"注:默认歌库为网易云ACG动画榜\n" +
|
||||
"可在后面添加[-歌单名称]进行指定歌单猜歌\n" +
|
||||
"歌单的歌曲命名规则为:\n歌名 - 歌手 - 其他(歌曲出处之类)",
|
||||
PrivateDataFolder: "guessmusic",
|
||||
}).ApplySingle(single.New(
|
||||
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
|
||||
@@ -84,11 +97,28 @@ func init() { // 插件主体
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
var plist = []listRaw{
|
||||
{
|
||||
Name: "动画榜",
|
||||
ID: 3001835560,
|
||||
},
|
||||
}
|
||||
cfg = config{ // 默认 config
|
||||
MusicPath: file.BOTPATH + "/data/guessmusic/music/", // 绝对路径,歌库根目录,通过指令进行更改
|
||||
Local: true, // 是否使用本地音乐库
|
||||
API: true, // 是否使用 Api
|
||||
Cookie: "",
|
||||
Playlist: plist,
|
||||
}
|
||||
err = saveConfig(cfgFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
err = getcatlist(cfg.MusicPath)
|
||||
if err != nil {
|
||||
logrus.Infof("[guessmusic2]无法获取歌单列表,[error]:%s", err)
|
||||
}
|
||||
engine.OnRegex(`^设置猜歌(缓存歌库路径|本地|Api)\s*(.*)$`, func(ctx *zero.Ctx) bool {
|
||||
if !zero.SuperUserPermission(ctx) {
|
||||
ctx.SendChain(message.Text("只有bot主人可以设置!"))
|
||||
@@ -109,6 +139,11 @@ func init() { // 插件主体
|
||||
if !strings.HasSuffix(musicPath, "/") {
|
||||
musicPath += "/"
|
||||
}
|
||||
err = os.MkdirAll(cfg.MusicPath, 0755)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("[生成文件夹错误]ERROR:", err))
|
||||
return
|
||||
}
|
||||
cfg.MusicPath = musicPath
|
||||
case "本地":
|
||||
choice, err := strconv.ParseBool(value)
|
||||
@@ -132,21 +167,312 @@ func init() { // 插件主体
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^(个人|团队)猜歌(-动漫|-动漫2)?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
engine.OnFullMatch("登录网易云", zero.SuperUserPermission, func(ctx *zero.Ctx) bool {
|
||||
if !zero.OnlyPrivate(ctx) {
|
||||
ctx.SendChain(message.Text("为了保护登录过程,请bot主人私聊。"))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}).SetBlock(true).
|
||||
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次机会"))
|
||||
keyURL := "https://music.cyrilstudio.top/login/qr/key"
|
||||
data, err := web.GetData(keyURL)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("获取网易云key失败, ERROR:", err))
|
||||
return
|
||||
}
|
||||
var keyInfo keyInfo
|
||||
err = json.Unmarshal(data, &keyInfo)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("解析网易云key失败, ERROR:", err))
|
||||
return
|
||||
}
|
||||
qrURL := "https://music.cyrilstudio.top/login/qr/create?key=" + keyInfo.Data.Unikey + "&qrimg=1"
|
||||
data, err = web.GetData(qrURL)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("获取网易云二维码失败, ERROR:", err))
|
||||
return
|
||||
}
|
||||
var qrInfo qrInfo
|
||||
err = json.Unmarshal(data, &qrInfo)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("解析网易云二维码失败, ERROR:", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("[请使用手机APP扫描二维码或者进入网页扫码登录]\n", qrInfo.Data.Qrurl),
|
||||
message.Image("base64://"+strings.ReplaceAll(qrInfo.Data.Qrimg, "data:image/png;base64,", "")),
|
||||
message.Text("二维码有效时间为6分钟,登陆后请耐心等待结果,获取cookie过程有些漫长。"))
|
||||
i := 0
|
||||
for range time.NewTicker(10 * time.Second).C {
|
||||
apiURL := "https://music.cyrilstudio.top/login/qr/check?key=" + url.QueryEscape(keyInfo.Data.Unikey)
|
||||
referer := "https://music.cyrilstudio.top"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("无法获取登录状态, ERROR:", err))
|
||||
return
|
||||
}
|
||||
var cookiesInfo cookyInfo
|
||||
err = json.Unmarshal(data, &cookiesInfo)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("解析登录状态失败, ERROR:", err))
|
||||
return
|
||||
}
|
||||
switch cookiesInfo.Code {
|
||||
case 803:
|
||||
cfg.Cookie = cookiesInfo.Cookie
|
||||
err = saveConfig(cfgFile)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
return
|
||||
case 801:
|
||||
i++
|
||||
if i%6 == 0 { // 每1分钟才提醒一次,减少提示(380/60=6次)
|
||||
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
|
||||
}
|
||||
continue
|
||||
case 800:
|
||||
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
|
||||
return
|
||||
default:
|
||||
ctx.SendChain(message.Text("状态:", cookiesInfo.Message))
|
||||
continue
|
||||
}
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^添加歌单\s?(\d+)(\s(.*))?$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
listID := ctx.State["regex_matched"].([]string)[1]
|
||||
listName := ctx.State["regex_matched"].([]string)[3]
|
||||
ctx.SendChain(message.Text("正在校验歌单信息,请稍等"))
|
||||
// 是否存在该歌单
|
||||
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listID + "&cookie=" + cfg.Cookie
|
||||
referer := "https://music.cyrilstudio.top"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("无法连接歌单,[error]", err))
|
||||
return
|
||||
}
|
||||
var parsed topList
|
||||
err = json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err))
|
||||
return
|
||||
}
|
||||
// 是否有权限访问歌单列表内容
|
||||
apiURL = "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + cfg.Cookie
|
||||
referer = "https://music.163.com/"
|
||||
data, err = web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("无法获取歌单列表\n ERROR:", err))
|
||||
return
|
||||
}
|
||||
var musiclist topMusicInfo
|
||||
err = json.Unmarshal(data, &musiclist)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("你的cookie在API中无权访问该歌单\n该歌单有可能是用户私人歌单"))
|
||||
return
|
||||
}
|
||||
// 获取列表名字
|
||||
if listName == "" {
|
||||
listName = parsed.Playlist.Name
|
||||
}
|
||||
playID, _ := strconv.ParseInt(listID, 10, 64)
|
||||
catlist[listName] = playID
|
||||
cfg.Playlist = append(cfg.Playlist, listRaw{
|
||||
Name: listName,
|
||||
ID: playID,
|
||||
})
|
||||
err = saveConfig(cfgFile)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^删除歌单\s?(.*)$`, zero.SuperUserPermission).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
delList := ctx.State["regex_matched"].([]string)[1]
|
||||
var playlist []listRaw
|
||||
var newCatList = make(map[string]int64)
|
||||
var ok = false
|
||||
for name, musicID := range catlist {
|
||||
if delList == name || delList == strconv.FormatInt(musicID, 10) {
|
||||
ok = true
|
||||
continue
|
||||
}
|
||||
newCatList[name] = musicID
|
||||
playlist = append(playlist, listRaw{
|
||||
Name: name,
|
||||
ID: musicID,
|
||||
})
|
||||
}
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("目标歌单未找到,请确认是否正确"))
|
||||
return
|
||||
}
|
||||
catlist = newCatList
|
||||
cfg.Playlist = playlist
|
||||
err = saveConfig(cfgFile)
|
||||
if err == nil {
|
||||
ctx.SendChain(message.Text("成功!"))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
})
|
||||
engine.OnFullMatch("获取歌单列表").SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
var msg []string
|
||||
// 获取网易云歌单列表
|
||||
if cfg.API {
|
||||
catlist = make(map[string]int64, 100)
|
||||
msg = append(msg, "当前添加的API歌单含有以下:\n")
|
||||
for i, listInfo := range cfg.Playlist {
|
||||
catlist[listInfo.Name] = listInfo.ID
|
||||
msg = append(msg, strconv.Itoa(i)+":"+listInfo.Name)
|
||||
if i%3 == 2 {
|
||||
msg = append(msg, "\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取本地歌单列表*/
|
||||
if cfg.Local {
|
||||
err = os.MkdirAll(cfg.MusicPath, 0755)
|
||||
if err == nil {
|
||||
files, err := ioutil.ReadDir(cfg.MusicPath)
|
||||
if err == nil {
|
||||
if len(files) == 0 {
|
||||
ctx.SendChain(message.Text("缓存目录没有读取到任何歌单"))
|
||||
filelist = nil
|
||||
} else {
|
||||
msg = append(msg, "\n当前本地歌单含有以下:\n")
|
||||
i := 0
|
||||
for _, name := range files {
|
||||
if !name.IsDir() {
|
||||
continue
|
||||
}
|
||||
filelist[i] = strconv.Itoa(i) + ":" + name.Name()
|
||||
msg = append(msg, filelist[i])
|
||||
if i%3 == 2 {
|
||||
msg = append(msg, "\n")
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.SendChain(message.Text("[读取本地列表错误]ERROR:", err))
|
||||
}
|
||||
} else {
|
||||
ctx.SendChain(message.Text("[生成文件夹错误]ERROR:", err))
|
||||
}
|
||||
}
|
||||
if msg == nil {
|
||||
ctx.SendChain(message.Text("本地和API均未开启!"))
|
||||
return
|
||||
}
|
||||
msgs, err := text.RenderToBase64(strings.Join(msg, " "), text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("生成列表图片失败,请重试"))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + helper.BytesToString(msgs))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
|
||||
}
|
||||
})
|
||||
engine.OnSuffix("歌单信息").SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
list := ctx.State["args"].(string)
|
||||
if list == "" {
|
||||
ctx.SendChain(message.Text("请输入歌单ID或者API歌单名称\n歌单ID为(网页/分享)链接的“playlist”后面的第一串数字"))
|
||||
return
|
||||
}
|
||||
var listIDStr string
|
||||
for listName, listID := range catlist {
|
||||
if list == listName || list == strconv.FormatInt(listID, 10) {
|
||||
listIDStr = strconv.FormatInt(listID, 10)
|
||||
break
|
||||
}
|
||||
}
|
||||
if listIDStr == "" {
|
||||
_, err := strconv.ParseInt(list, 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("仅支持歌单ID查询"))
|
||||
return
|
||||
}
|
||||
listIDStr = list
|
||||
}
|
||||
apiURL := "https://music.cyrilstudio.top/playlist/detail?id=" + listIDStr + "&cookie=" + cfg.Cookie
|
||||
referer := "https://music.cyrilstudio.top"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("无法连接歌单,[error]", err))
|
||||
return
|
||||
}
|
||||
var parsed topList
|
||||
err = json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("无法解析歌单ID内容,[error]", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(
|
||||
message.Image(parsed.Playlist.CoverImgURL),
|
||||
message.Text(
|
||||
"歌单名称:", parsed.Playlist.Name,
|
||||
"\n歌单ID:", parsed.Playlist.ID,
|
||||
"\n创建人:", parsed.Playlist.Creator.Nickname,
|
||||
"\n创建时间:", time.Unix(parsed.Playlist.CreateTime/1000, 0).Format("2006-01-02"),
|
||||
"\n标签:", strings.Join(parsed.Playlist.Tags, ";"),
|
||||
"\n歌曲数量:", parsed.Playlist.TrackCount,
|
||||
"\n歌单简介:\n", parsed.Playlist.Description,
|
||||
"\n更新时间:", time.Unix(parsed.Playlist.UpdateTime/1000, 0).Format("2006-01-02"),
|
||||
))
|
||||
})
|
||||
engine.OnRegex(`^(个人|团队)猜歌(-(.*))?$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByGroup).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
mode := ctx.State["regex_matched"].([]string)[3]
|
||||
if mode == "" {
|
||||
mode = "动画榜"
|
||||
catlist[mode] = 3001835560
|
||||
}
|
||||
_, ok := catlist[mode]
|
||||
// 如果本地和API不存在该歌单
|
||||
if !strings.Contains(strings.Join(filelist, " "), mode) && !ok {
|
||||
ctx.SendChain(message.Text("歌单名称错误,可以发送“获取歌单列表”获取歌单名称"))
|
||||
return
|
||||
}
|
||||
gid := strconv.FormatInt(ctx.Event.GroupID, 10)
|
||||
ctx.SendChain(message.Text("正在准备歌曲,请稍等\n回答“-[歌曲信息(歌名歌手等)|提示|取消]”\n一共3段语音,6次机会"))
|
||||
// 随机抽歌
|
||||
musicName, pathOfMusic, err := musicLottery(mode, cfg.MusicPath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text(err))
|
||||
return
|
||||
}
|
||||
// 解析歌曲信息
|
||||
music := strings.Split(musicName, ".")
|
||||
// 获取音乐后缀
|
||||
musictype := music[len(music)-1]
|
||||
if !strings.Contains(musictypelist, musictype) {
|
||||
ctx.SendChain(message.Text("抽取到了本地歌曲:\n",
|
||||
musicName, "\n该歌曲不是音乐后缀,请联系bot主人修改"))
|
||||
return
|
||||
}
|
||||
// 获取音乐信息
|
||||
musicInfo := strings.Split(strings.ReplaceAll(musicName, "."+musictype, ""), " - ")
|
||||
infoNum := len(musicInfo)
|
||||
if infoNum == 1 {
|
||||
ctx.SendChain(message.Text("抽取到了本地歌曲:\n",
|
||||
musicName, "\n该歌曲命名不符合命名规则,请联系bot主人修改"))
|
||||
return
|
||||
}
|
||||
answerString := "歌名:" + musicInfo[0] + "\n歌手:" + musicInfo[1]
|
||||
musicAlia := ""
|
||||
if infoNum > 2 {
|
||||
musicAlia = musicInfo[2]
|
||||
answerString += "\n其他信息:\n" + strings.ReplaceAll(musicAlia, "&", "\n")
|
||||
}
|
||||
// 切割音频,生成3个10秒的音频
|
||||
outputPath := cachePath + gid + "/"
|
||||
err = cutMusic(musicName, pathOfMusic, outputPath)
|
||||
@@ -156,7 +482,6 @@ func init() { // 插件主体
|
||||
}
|
||||
// 进行猜歌环节
|
||||
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())
|
||||
@@ -175,15 +500,8 @@ func init() { // 插件主体
|
||||
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)
|
||||
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
|
||||
message.Text("时间超时,猜歌结束,公布答案:\n", answerString)))
|
||||
return
|
||||
case <-wait.C:
|
||||
wait.Reset(40 * time.Second)
|
||||
@@ -207,15 +525,8 @@ func init() { // 插件主体
|
||||
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)
|
||||
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID,
|
||||
message.Text("游戏已取消,猜歌答案是\n", answerString)))
|
||||
return
|
||||
}
|
||||
ctx.Send(
|
||||
@@ -241,53 +552,28 @@ func init() { // 插件主体
|
||||
),
|
||||
)
|
||||
ctx.SendChain(message.Record("file:///" + file.BOTPATH + "/" + outputPath + strconv.Itoa(musicCount) + ".wav"))
|
||||
case strings.Contains(answerString[0], answer) || strings.EqualFold(answerString[0], answer):
|
||||
case strings.Contains(musicInfo[0], answer) || strings.EqualFold(musicInfo[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)
|
||||
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("太棒了,你猜对歌曲名了!答案是\n", answerString)))
|
||||
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):
|
||||
case strings.Contains(musicInfo[1], answer) || strings.EqualFold(musicInfo[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)
|
||||
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("太棒了,你猜对歌手名了!答案是\n", answerString)))
|
||||
return
|
||||
case strings.Contains(musicAlia, answer) || strings.EqualFold(musicAlia, answer):
|
||||
wait.Stop()
|
||||
tick.Stop()
|
||||
after.Stop()
|
||||
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("太棒了,你猜对出处了!答案是\n", answerString)))
|
||||
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:
|
||||
@@ -302,15 +588,8 @@ func init() { // 插件主体
|
||||
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)
|
||||
ctx.Send(message.ReplyWithMessage(c.Event.MessageID,
|
||||
message.Text("次数到了,没能猜出来。答案是\n", answerString)))
|
||||
return
|
||||
default:
|
||||
wait.Reset(40 * time.Second)
|
||||
@@ -340,16 +619,30 @@ func saveConfig(cfgFile string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getcatlist(pathOfMusic string) error {
|
||||
catlist = make(map[string]int64, 100)
|
||||
for _, listInfo := range cfg.Playlist {
|
||||
catlist[listInfo.Name] = listInfo.ID
|
||||
}
|
||||
err := os.MkdirAll(pathOfMusic, 0755)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
|
||||
return err
|
||||
}
|
||||
files, err := ioutil.ReadDir(pathOfMusic)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
|
||||
return err
|
||||
}
|
||||
for i, name := range files {
|
||||
filelist = append(filelist, strconv.Itoa(i)+":"+name.Name())
|
||||
}
|
||||
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 + "歌榜/"
|
||||
}
|
||||
pathOfMusic = musicPath + mode + "/"
|
||||
err = os.MkdirAll(pathOfMusic, 0755)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[生成文件夹错误]ERROR:%s", err)
|
||||
@@ -360,21 +653,27 @@ func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err er
|
||||
err = errors.Errorf("[读取本地列表错误]ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
listID, ok := catlist[mode]
|
||||
listIDstr := strconv.FormatInt(listID, 10)
|
||||
if cfg.Local && cfg.API {
|
||||
switch {
|
||||
case len(files) == 0:
|
||||
if !ok {
|
||||
// 如果歌单是本地歌单
|
||||
err = errors.New("本地歌单数据为0")
|
||||
return
|
||||
}
|
||||
// 如果没有任何本地就下载歌曲
|
||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
||||
musicName, err = getListMusic(listIDstr, pathOfMusic)
|
||||
if err != nil {
|
||||
err = errors.Errorf("[本地数据为0,歌曲下载错误]ERROR:%s", err)
|
||||
return
|
||||
}
|
||||
case rand.Intn(2) == 0:
|
||||
// [0,1)只会取到0,rand不允许的
|
||||
case rand.Intn(2) == 0 || !ok:
|
||||
// 1/2概率抽本地或者歌单只有本地有时
|
||||
musicName = getLocalMusic(files)
|
||||
default:
|
||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
||||
musicName, err = getListMusic(listIDstr, pathOfMusic)
|
||||
if err != nil {
|
||||
// 如果下载失败就从本地抽一个歌曲
|
||||
musicName = getLocalMusic(files)
|
||||
@@ -391,173 +690,82 @@ func musicLottery(mode, musicPath string) (musicName, pathOfMusic string, err er
|
||||
musicName = getLocalMusic(files)
|
||||
return
|
||||
}
|
||||
if cfg.API {
|
||||
musicName, err = getAPIMusic(mode, pathOfMusic)
|
||||
if cfg.API && ok {
|
||||
musicName, err = getListMusic(listIDstr, 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)
|
||||
}
|
||||
err = errors.New("[请确认本地和API设置已开启或歌单存在]")
|
||||
return
|
||||
}
|
||||
|
||||
func getLocalMusic(files []fs.FileInfo) (musicName string) {
|
||||
if len(files) > 1 {
|
||||
musicName = strings.Replace(files[rand.Intn(len(files))].Name(), ".mp3", "", 1)
|
||||
musicName = files[rand.Intn(len(files))].Name()
|
||||
} else {
|
||||
musicName = strings.Replace(files[0].Name(), ".mp3", "", 1)
|
||||
musicName = files[0].Name()
|
||||
}
|
||||
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)
|
||||
// 下载网易云歌单音乐
|
||||
func getListMusic(listID, pathOfMusic string) (musicName string, err error) {
|
||||
apiURL := "https://music.cyrilstudio.top/playlist/track/all?id=" + listID + "&cookie=" + cfg.Cookie
|
||||
referer := "https://music.163.com/"
|
||||
data, err := web.RequestDataWith(web.NewDefaultClient(), apiURL, "GET", referer, ua)
|
||||
if err != nil {
|
||||
err = errors.Errorf("无法获取歌单列表\n ERROR: %s", err)
|
||||
return
|
||||
}
|
||||
var parsed paugramData
|
||||
var parsed topMusicInfo
|
||||
err = json.Unmarshal(data, &parsed)
|
||||
if err != nil {
|
||||
err = errors.Errorf("无法读取歌单列表\n ERROR: %s", err)
|
||||
return
|
||||
}
|
||||
name := parsed.Title
|
||||
artistsName := parsed.Artist
|
||||
musicURL := parsed.Link
|
||||
if name == "" || artistsName == "" {
|
||||
listlen := len(parsed.Songs)
|
||||
randidx := rand.Intn(listlen)
|
||||
// 将"/"符号去除,不然无法生成文件
|
||||
name := strings.ReplaceAll(parsed.Songs[randidx].Name, "/", "·")
|
||||
musicID := parsed.Songs[randidx].ID
|
||||
artistName := ""
|
||||
for i, ARInfo := range parsed.Songs[randidx].Ar {
|
||||
if i != 0 {
|
||||
artistName += "&" + ARInfo.Name
|
||||
} else {
|
||||
artistName += ARInfo.Name
|
||||
}
|
||||
}
|
||||
cource := ""
|
||||
if parsed.Songs[randidx].Alia != nil {
|
||||
cource = strings.Join(parsed.Songs[randidx].Alia, "&")
|
||||
// 将"/"符号去除,不然无法下载
|
||||
cource = strings.ReplaceAll(cource, "/", "&")
|
||||
}
|
||||
if name == "" || musicID == 0 {
|
||||
err = errors.New("无法获API取歌曲信息")
|
||||
return
|
||||
}
|
||||
musicName = name + " - " + artistsName
|
||||
downMusic := musicPath + "/" + musicName + ".mp3"
|
||||
musicURL := "http://music.163.com/song/media/outer/url?id=" + strconv.Itoa(musicID)
|
||||
response, err := http.Head(musicURL)
|
||||
if err != nil {
|
||||
err = errors.Errorf("下载音乐失败, ERROR: %s", err)
|
||||
return
|
||||
}
|
||||
_ = response.Body.Close()
|
||||
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
|
||||
}
|
||||
if cource != "" {
|
||||
musicName = name + " - " + artistName + " - " + cource + ".mp3"
|
||||
} else {
|
||||
musicName = name + " - " + artistName + ".mp3"
|
||||
}
|
||||
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"
|
||||
downMusic := pathOfMusic + musicName
|
||||
if file.IsNotExist(downMusic) {
|
||||
data, err = web.GetData(musicURL)
|
||||
if err != nil {
|
||||
@@ -579,7 +787,7 @@ func cutMusic(musicName, pathOfMusic, outputPath string) (err error) {
|
||||
return
|
||||
}
|
||||
var stderr bytes.Buffer
|
||||
cmdArguments := []string{"-y", "-i", pathOfMusic + musicName + ".mp3",
|
||||
cmdArguments := []string{"-y", "-i", pathOfMusic + musicName,
|
||||
"-ss", cuttime[0], "-t", "10", file.BOTPATH + "/" + outputPath + "0.wav",
|
||||
"-ss", cuttime[1], "-t", "10", file.BOTPATH + "/" + outputPath + "1.wav",
|
||||
"-ss", cuttime[2], "-t", "10", file.BOTPATH + "/" + outputPath + "2.wav", "-hide_banner"}
|
||||
|
||||
@@ -1,107 +1,406 @@
|
||||
package guessmusic
|
||||
|
||||
type listRaw struct {
|
||||
Name string `json:"name"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type config struct {
|
||||
MusicPath string `json:"musicPath"`
|
||||
Local bool `json:"local"`
|
||||
API bool `json:"api"`
|
||||
MusicPath string `json:"musicPath"`
|
||||
Local bool `json:"local"`
|
||||
API bool `json:"api"`
|
||||
Cookie string `json:"cookie"`
|
||||
Playlist []listRaw `json:"playlist"`
|
||||
}
|
||||
|
||||
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"`
|
||||
type keyInfo struct {
|
||||
Data struct {
|
||||
Code int `json:"code"`
|
||||
Unikey string `json:"unikey"`
|
||||
} `json:"data"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
type netEaseData struct {
|
||||
type cookyInfo struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Cookie string `json:"cookie"`
|
||||
}
|
||||
type qrInfo struct {
|
||||
Code int `json:"code"`
|
||||
Data struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Picurl string `json:"picurl"`
|
||||
Artistsname string `json:"artistsname"`
|
||||
Qrurl string `json:"qrurl"`
|
||||
Qrimg string `json:"qrimg"`
|
||||
} `json:"data"`
|
||||
}
|
||||
type topList struct {
|
||||
Code int `json:"code"`
|
||||
RelatedVideos interface{} `json:"relatedVideos"`
|
||||
Playlist struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
CoverImgID int64 `json:"coverImgId"`
|
||||
CoverImgURL string `json:"coverImgUrl"`
|
||||
CoverImgIDStr string `json:"coverImgId_str"`
|
||||
AdType int `json:"adType"`
|
||||
UserID int `json:"userId"`
|
||||
CreateTime int64 `json:"createTime"`
|
||||
Status int `json:"status"`
|
||||
OpRecommend bool `json:"opRecommend"`
|
||||
HighQuality bool `json:"highQuality"`
|
||||
NewImported bool `json:"newImported"`
|
||||
UpdateTime int64 `json:"updateTime"`
|
||||
TrackCount int `json:"trackCount"`
|
||||
SpecialType int `json:"specialType"`
|
||||
Privacy int `json:"privacy"`
|
||||
TrackUpdateTime int64 `json:"trackUpdateTime"`
|
||||
CommentThreadID string `json:"commentThreadId"`
|
||||
PlayCount int `json:"playCount"`
|
||||
TrackNumberUpdateTime int64 `json:"trackNumberUpdateTime"`
|
||||
SubscribedCount int `json:"subscribedCount"`
|
||||
CloudTrackCount int `json:"cloudTrackCount"`
|
||||
Ordered bool `json:"ordered"`
|
||||
Description string `json:"description"`
|
||||
Tags []string `json:"tags"`
|
||||
UpdateFrequency interface{} `json:"updateFrequency"`
|
||||
BackgroundCoverID int `json:"backgroundCoverId"`
|
||||
BackgroundCoverURL interface{} `json:"backgroundCoverUrl"`
|
||||
TitleImage int `json:"titleImage"`
|
||||
TitleImageURL interface{} `json:"titleImageUrl"`
|
||||
EnglishTitle interface{} `json:"englishTitle"`
|
||||
OfficialPlaylistType interface{} `json:"officialPlaylistType"`
|
||||
Subscribers []struct {
|
||||
DefaultAvatar bool `json:"defaultAvatar"`
|
||||
Province int `json:"province"`
|
||||
AuthStatus int `json:"authStatus"`
|
||||
Followed bool `json:"followed"`
|
||||
AvatarURL string `json:"avatarUrl"`
|
||||
AccountStatus int `json:"accountStatus"`
|
||||
Gender int `json:"gender"`
|
||||
City int `json:"city"`
|
||||
Birthday int `json:"birthday"`
|
||||
UserID int `json:"userId"`
|
||||
UserType int `json:"userType"`
|
||||
Nickname string `json:"nickname"`
|
||||
Signature string `json:"signature"`
|
||||
Description string `json:"description"`
|
||||
DetailDescription string `json:"detailDescription"`
|
||||
AvatarImgID int64 `json:"avatarImgId"`
|
||||
BackgroundImgID int64 `json:"backgroundImgId"`
|
||||
BackgroundURL string `json:"backgroundUrl"`
|
||||
Authority int `json:"authority"`
|
||||
Mutual bool `json:"mutual"`
|
||||
ExpertTags interface{} `json:"expertTags"`
|
||||
Experts interface{} `json:"experts"`
|
||||
DjStatus int `json:"djStatus"`
|
||||
VipType int `json:"vipType"`
|
||||
RemarkName interface{} `json:"remarkName"`
|
||||
AuthenticationTypes int `json:"authenticationTypes"`
|
||||
AvatarDetail interface{} `json:"avatarDetail"`
|
||||
Anchor bool `json:"anchor"`
|
||||
BackgroundImgIDStr string `json:"backgroundImgIdStr"`
|
||||
AvatarImgIDStr string `json:"avatarImgIdStr"`
|
||||
AvatarImgIDString string `json:"AvatarImgIDString"`
|
||||
} `json:"subscribers"`
|
||||
Subscribed interface{} `json:"subscribed"`
|
||||
Creator struct {
|
||||
DefaultAvatar bool `json:"defaultAvatar"`
|
||||
Province int `json:"province"`
|
||||
AuthStatus int `json:"authStatus"`
|
||||
Followed bool `json:"followed"`
|
||||
AvatarURL string `json:"avatarUrl"`
|
||||
AccountStatus int `json:"accountStatus"`
|
||||
Gender int `json:"gender"`
|
||||
City int `json:"city"`
|
||||
Birthday int `json:"birthday"`
|
||||
UserID int `json:"userId"`
|
||||
UserType int `json:"userType"`
|
||||
Nickname string `json:"nickname"`
|
||||
Signature string `json:"signature"`
|
||||
Description string `json:"description"`
|
||||
DetailDescription string `json:"detailDescription"`
|
||||
AvatarImgID int64 `json:"avatarImgId"`
|
||||
BackgroundImgID int64 `json:"backgroundImgId"`
|
||||
BackgroundURL string `json:"backgroundUrl"`
|
||||
Authority int `json:"authority"`
|
||||
Mutual bool `json:"mutual"`
|
||||
ExpertTags interface{} `json:"expertTags"`
|
||||
Experts interface{} `json:"experts"`
|
||||
DjStatus int `json:"djStatus"`
|
||||
VipType int `json:"vipType"`
|
||||
RemarkName interface{} `json:"remarkName"`
|
||||
AuthenticationTypes int `json:"authenticationTypes"`
|
||||
AvatarDetail struct {
|
||||
UserType int `json:"userType"`
|
||||
IdentityLevel int `json:"identityLevel"`
|
||||
IdentityIconURL string `json:"identityIconUrl"`
|
||||
} `json:"avatarDetail"`
|
||||
Anchor bool `json:"anchor"`
|
||||
BackgroundImgIDStr string `json:"backgroundImgIdStr"`
|
||||
AvatarImgIDStr string `json:"avatarImgIdStr"`
|
||||
AvatarImgIDString string `json:"AvatarImgIDString"`
|
||||
} `json:"creator"`
|
||||
Tracks []struct {
|
||||
Name string `json:"name"`
|
||||
ID int `json:"id"`
|
||||
Pst int `json:"pst"`
|
||||
T int `json:"t"`
|
||||
Ar []struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Tns []interface{} `json:"tns"`
|
||||
Alias []interface{} `json:"alias"`
|
||||
} `json:"ar"`
|
||||
Alia []string `json:"alia"`
|
||||
Pop int `json:"pop"`
|
||||
St int `json:"st"`
|
||||
Rt string `json:"rt"`
|
||||
Fee int `json:"fee"`
|
||||
V int `json:"v"`
|
||||
Crbt interface{} `json:"crbt"`
|
||||
Cf string `json:"cf"`
|
||||
Al struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
PicURL string `json:"picUrl"`
|
||||
Tns []interface{} `json:"tns"`
|
||||
PicStr string `json:"pic_str"`
|
||||
Pic int64 `json:"pic"`
|
||||
} `json:"al"`
|
||||
Dt int `json:"dt"`
|
||||
H struct {
|
||||
Br int `json:"br"`
|
||||
Fid int `json:"fid"`
|
||||
Size int `json:"size"`
|
||||
Vd float64 `json:"vd"`
|
||||
Sr int `json:"sr"`
|
||||
} `json:"h"`
|
||||
M struct {
|
||||
Br int `json:"br"`
|
||||
Fid int `json:"fid"`
|
||||
Size int `json:"size"`
|
||||
Vd float64 `json:"vd"`
|
||||
Sr int `json:"sr"`
|
||||
} `json:"m"`
|
||||
L struct {
|
||||
Br int `json:"br"`
|
||||
Fid int `json:"fid"`
|
||||
Size int `json:"size"`
|
||||
Vd float64 `json:"vd"`
|
||||
Sr int `json:"sr"`
|
||||
} `json:"l"`
|
||||
Sq interface{} `json:"sq"`
|
||||
Hr interface{} `json:"hr"`
|
||||
A interface{} `json:"a"`
|
||||
Cd string `json:"cd"`
|
||||
No int `json:"no"`
|
||||
RtURL interface{} `json:"rtUrl"`
|
||||
Ftype int `json:"ftype"`
|
||||
RtUrls []interface{} `json:"rtUrls"`
|
||||
DjID int `json:"djId"`
|
||||
Copyright int `json:"copyright"`
|
||||
SID int `json:"s_id"`
|
||||
Mark int `json:"mark"`
|
||||
OriginCoverType int `json:"originCoverType"`
|
||||
OriginSongSimpleData interface{} `json:"originSongSimpleData"`
|
||||
TagPicList interface{} `json:"tagPicList"`
|
||||
ResourceState bool `json:"resourceState"`
|
||||
Version int `json:"version"`
|
||||
SongJumpInfo interface{} `json:"songJumpInfo"`
|
||||
EntertainmentTags interface{} `json:"entertainmentTags"`
|
||||
Single int `json:"single"`
|
||||
NoCopyrightRcmd interface{} `json:"noCopyrightRcmd"`
|
||||
Alg interface{} `json:"alg"`
|
||||
Rtype int `json:"rtype"`
|
||||
Rurl interface{} `json:"rurl"`
|
||||
Mst int `json:"mst"`
|
||||
Cp int `json:"cp"`
|
||||
Mv int `json:"mv"`
|
||||
PublishTime int64 `json:"publishTime"`
|
||||
Tns []string `json:"tns,omitempty"`
|
||||
} `json:"tracks"`
|
||||
VideoIds interface{} `json:"videoIds"`
|
||||
Videos interface{} `json:"videos"`
|
||||
TrackIds []struct {
|
||||
ID int `json:"id"`
|
||||
V int `json:"v"`
|
||||
T int `json:"t"`
|
||||
At int64 `json:"at"`
|
||||
Alg interface{} `json:"alg"`
|
||||
UID int `json:"uid"`
|
||||
RcmdReason string `json:"rcmdReason"`
|
||||
Sc interface{} `json:"sc"`
|
||||
Lr int `json:"lr,omitempty"`
|
||||
} `json:"trackIds"`
|
||||
ShareCount int `json:"shareCount"`
|
||||
CommentCount int `json:"commentCount"`
|
||||
RemixVideo interface{} `json:"remixVideo"`
|
||||
SharedUsers interface{} `json:"sharedUsers"`
|
||||
HistorySharedUsers interface{} `json:"historySharedUsers"`
|
||||
GradeStatus string `json:"gradeStatus"`
|
||||
Score interface{} `json:"score"`
|
||||
AlgTags interface{} `json:"algTags"`
|
||||
} `json:"playlist"`
|
||||
Urls interface{} `json:"urls"`
|
||||
Privileges []struct {
|
||||
ID int `json:"id"`
|
||||
Fee int `json:"fee"`
|
||||
Payed int `json:"payed"`
|
||||
RealPayed int `json:"realPayed"`
|
||||
St int `json:"st"`
|
||||
Pl int `json:"pl"`
|
||||
Dl int `json:"dl"`
|
||||
Sp int `json:"sp"`
|
||||
Cp int `json:"cp"`
|
||||
Subp int `json:"subp"`
|
||||
Cs bool `json:"cs"`
|
||||
Maxbr int `json:"maxbr"`
|
||||
Fl int `json:"fl"`
|
||||
Pc interface{} `json:"pc"`
|
||||
Toast bool `json:"toast"`
|
||||
Flag int `json:"flag"`
|
||||
PaidBigBang bool `json:"paidBigBang"`
|
||||
PreSell bool `json:"preSell"`
|
||||
PlayMaxbr int `json:"playMaxbr"`
|
||||
DownloadMaxbr int `json:"downloadMaxbr"`
|
||||
MaxBrLevel string `json:"maxBrLevel"`
|
||||
PlayMaxBrLevel string `json:"playMaxBrLevel"`
|
||||
DownloadMaxBrLevel string `json:"downloadMaxBrLevel"`
|
||||
PlLevel string `json:"plLevel"`
|
||||
DlLevel string `json:"dlLevel"`
|
||||
FlLevel string `json:"flLevel"`
|
||||
Rscl int `json:"rscl"`
|
||||
FreeTrialPrivilege struct {
|
||||
ResConsumable bool `json:"resConsumable"`
|
||||
UserConsumable bool `json:"userConsumable"`
|
||||
ListenType interface{} `json:"listenType"`
|
||||
} `json:"freeTrialPrivilege"`
|
||||
ChargeInfoList []struct {
|
||||
Rate int `json:"rate"`
|
||||
ChargeURL interface{} `json:"chargeUrl"`
|
||||
ChargeMessage interface{} `json:"chargeMessage"`
|
||||
ChargeType int `json:"chargeType"`
|
||||
} `json:"chargeInfoList"`
|
||||
} `json:"privileges"`
|
||||
SharedPrivilege interface{} `json:"sharedPrivilege"`
|
||||
ResEntrance interface{} `json:"resEntrance"`
|
||||
}
|
||||
|
||||
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"`
|
||||
type topMusicInfo struct {
|
||||
Songs []struct {
|
||||
Name string `json:"name"`
|
||||
ID int `json:"id"`
|
||||
Pst int `json:"pst"`
|
||||
T int `json:"t"`
|
||||
Ar []struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Tns []interface{} `json:"tns"`
|
||||
Alias []interface{} `json:"alias"`
|
||||
} `json:"ar"`
|
||||
Alia []string `json:"alia"`
|
||||
Pop int `json:"pop"`
|
||||
St int `json:"st"`
|
||||
Rt string `json:"rt"`
|
||||
Fee int `json:"fee"`
|
||||
V int `json:"v"`
|
||||
Crbt interface{} `json:"crbt"`
|
||||
Cf string `json:"cf"`
|
||||
Al struct {
|
||||
ID int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
PicURL string `json:"picUrl"`
|
||||
Tns []interface{} `json:"tns"`
|
||||
PicStr string `json:"pic_str"`
|
||||
Pic int64 `json:"pic"`
|
||||
} `json:"al"`
|
||||
Dt int `json:"dt"`
|
||||
H struct {
|
||||
Br int `json:"br"`
|
||||
Fid int `json:"fid"`
|
||||
Size int `json:"size"`
|
||||
Vd float32 `json:"vd"`
|
||||
Sr int `json:"sr"`
|
||||
} `json:"h"`
|
||||
M struct {
|
||||
Br int `json:"br"`
|
||||
Fid int `json:"fid"`
|
||||
Size int `json:"size"`
|
||||
Vd float32 `json:"vd"`
|
||||
Sr int `json:"sr"`
|
||||
} `json:"m"`
|
||||
L struct {
|
||||
Br int `json:"br"`
|
||||
Fid int `json:"fid"`
|
||||
Size int `json:"size"`
|
||||
Vd float32 `json:"vd"`
|
||||
Sr int `json:"sr"`
|
||||
} `json:"l"`
|
||||
Sq interface{} `json:"sq"`
|
||||
Hr interface{} `json:"hr"`
|
||||
A interface{} `json:"a"`
|
||||
Cd string `json:"cd"`
|
||||
No int `json:"no"`
|
||||
RtURL interface{} `json:"rtUrl"`
|
||||
Ftype int `json:"ftype"`
|
||||
RtUrls []interface{} `json:"rtUrls"`
|
||||
DjID int `json:"djId"`
|
||||
Copyright int `json:"copyright"`
|
||||
SID int `json:"s_id"`
|
||||
Mark int `json:"mark"`
|
||||
OriginCoverType int `json:"originCoverType"`
|
||||
OriginSongSimpleData interface{} `json:"originSongSimpleData"`
|
||||
TagPicList interface{} `json:"tagPicList"`
|
||||
ResourceState bool `json:"resourceState"`
|
||||
Version int `json:"version"`
|
||||
SongJumpInfo interface{} `json:"songJumpInfo"`
|
||||
EntertainmentTags interface{} `json:"entertainmentTags"`
|
||||
AwardTags interface{} `json:"awardTags"`
|
||||
Single int `json:"single"`
|
||||
NoCopyrightRcmd interface{} `json:"noCopyrightRcmd"`
|
||||
Rtype int `json:"rtype"`
|
||||
Rurl interface{} `json:"rurl"`
|
||||
Mst int `json:"mst"`
|
||||
Cp int `json:"cp"`
|
||||
Mv int `json:"mv"`
|
||||
PublishTime int64 `json:"publishTime"`
|
||||
Tns []string `json:"tns,omitempty"`
|
||||
} `json:"songs"`
|
||||
Privileges []struct {
|
||||
ID int `json:"id"`
|
||||
Fee int `json:"fee"`
|
||||
Payed int `json:"payed"`
|
||||
St int `json:"st"`
|
||||
Pl int `json:"pl"`
|
||||
Dl int `json:"dl"`
|
||||
Sp int `json:"sp"`
|
||||
Cp int `json:"cp"`
|
||||
Subp int `json:"subp"`
|
||||
Cs bool `json:"cs"`
|
||||
Maxbr int `json:"maxbr"`
|
||||
Fl int `json:"fl"`
|
||||
Toast bool `json:"toast"`
|
||||
Flag int `json:"flag"`
|
||||
PreSell bool `json:"preSell"`
|
||||
PlayMaxbr int `json:"playMaxbr"`
|
||||
DownloadMaxbr int `json:"downloadMaxbr"`
|
||||
MaxBrLevel string `json:"maxBrLevel"`
|
||||
PlayMaxBrLevel string `json:"playMaxBrLevel"`
|
||||
DownloadMaxBrLevel string `json:"downloadMaxBrLevel"`
|
||||
PlLevel string `json:"plLevel"`
|
||||
DlLevel string `json:"dlLevel"`
|
||||
FlLevel string `json:"flLevel"`
|
||||
Rscl int `json:"rscl"`
|
||||
FreeTrialPrivilege struct {
|
||||
ResConsumable bool `json:"resConsumable"`
|
||||
UserConsumable bool `json:"userConsumable"`
|
||||
ListenType interface{} `json:"listenType"`
|
||||
} `json:"freeTrialPrivilege"`
|
||||
ChargeInfoList []struct {
|
||||
Rate int `json:"rate"`
|
||||
ChargeURL interface{} `json:"chargeUrl"`
|
||||
ChargeMessage interface{} `json:"chargeMessage"`
|
||||
ChargeType int `json:"chargeType"`
|
||||
} `json:"chargeInfoList"`
|
||||
} `json:"privileges"`
|
||||
Code int `json:"code"`
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
func init() {
|
||||
engine := control.Register("midicreate", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
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" +
|
||||
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?inline=false, windows需要管理员模式运行\n" +
|
||||
"- midi制作 CCGGAAGR FFEEDDCR GGFFEEDR GGFFEEDR CCGGAAGR FFEEDDCR\n" +
|
||||
"- 个人听音练习\n" +
|
||||
"- 团队听音练习\n" +
|
||||
|
||||
45
plugin/moegoe/main.go
Normal file
45
plugin/moegoe/main.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Package moegoe 日韩 VITS 模型拟声
|
||||
package moegoe
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
const (
|
||||
jpapi = "https://moegoe.azurewebsites.net/api/speak?text=%s&id=%d"
|
||||
krapi = "https://moegoe.azurewebsites.net/api/speakkr?text=%s&id=%d"
|
||||
)
|
||||
|
||||
var speakers = map[string]uint{
|
||||
"宁宁": 0, "爱瑠": 1, "芳乃": 2, "茉子": 3, "丛雨": 4, "小春": 5, "七海": 6,
|
||||
"Sua": 0, "Mimiru": 1, "Arin": 2, "Yeonhwa": 3, "Yuhwa": 4, "Seonbae": 5,
|
||||
}
|
||||
|
||||
func init() {
|
||||
en := control.Register("moegoe", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "moegoe\n" +
|
||||
"- 让[宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海]说(日语)\n" +
|
||||
"- 让[Sua|Mimiru|Arin|Yeonhwa|Yuhwa|Seonbae]说(韩语)",
|
||||
}).ApplySingle(ctxext.DefaultSingle)
|
||||
en.OnRegex("^让(宁宁|爱瑠|芳乃|茉子|丛雨|小春|七海)说([A-Za-z\\s\\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d.。,,、::;;!!??]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[2]
|
||||
id := speakers[ctx.State["regex_matched"].([]string)[1]]
|
||||
ctx.SendChain(message.Record(fmt.Sprintf(jpapi, url.QueryEscape(text), id)))
|
||||
})
|
||||
en.OnRegex("^让(Sua|Mimiru|Arin|Yeonhwa|Yuhwa|Seonbae)说([A-Za-z\\s\\d\u3131-\u3163\uac00-\ud7ff.。,,、::;;!!??]+)$").Limit(ctxext.LimitByGroup).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[2]
|
||||
id := speakers[ctx.State["regex_matched"].([]string)[1]]
|
||||
ctx.SendChain(message.Record(fmt.Sprintf(krapi, url.QueryEscape(text), id)))
|
||||
})
|
||||
}
|
||||
@@ -31,15 +31,15 @@ func TestSetHoliday(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = SetHoliday("清明节", 1, 2022, 4, 3)
|
||||
err = SetHoliday("清明节", 1, 2023, 4, 5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = SetHoliday("劳动节", 1, 2022, 4, 30)
|
||||
err = SetHoliday("劳动节", 1, 2023, 5, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = SetHoliday("端午节", 1, 2022, 6, 3)
|
||||
err = SetHoliday("端午节", 1, 2023, 6, 22)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ func kugou(keyword string) message.MessageSegment {
|
||||
|
||||
// cloud163 返回网易云音乐卡片
|
||||
func cloud163(keyword string) (msg message.MessageSegment) {
|
||||
requestURL := "https://autumnfish.cn/search?keywords=" + url.QueryEscape(keyword)
|
||||
requestURL := "https://music.cyrilstudio.top/search?keywords=" + url.QueryEscape(keyword)
|
||||
data, err := web.GetData(requestURL)
|
||||
if err != nil {
|
||||
msg = message.Text("ERROR:", err)
|
||||
|
||||
30
plugin/nihongo/model.go
Normal file
30
plugin/nihongo/model.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package nihongo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
)
|
||||
|
||||
type grammar struct {
|
||||
ID int `db:"id"`
|
||||
Tag string `db:"tag"`
|
||||
Name string `db:"name"`
|
||||
Pronunciation string `db:"pronunciation"`
|
||||
Usage string `db:"usage"`
|
||||
Meaning string `db:"meaning"`
|
||||
Explanation string `db:"explanation"`
|
||||
Example string `db:"example"`
|
||||
GrammarURL string `db:"grammar_url"`
|
||||
}
|
||||
|
||||
func (g *grammar) string() string {
|
||||
return fmt.Sprintf("ID:\n%d\n\n标签:\n%s\n\n语法名:\n%s\n\n发音:\n%s\n\n用法:\n%s\n\n意思:\n%s\n\n解说:\n%s\n\n示例:\n%s", g.ID, g.Tag, g.Name, g.Pronunciation, g.Usage, g.Meaning, g.Explanation, g.Example)
|
||||
}
|
||||
|
||||
var db = &sql.Sqlite{}
|
||||
|
||||
func getRandomGrammarByTag(tag string) (g grammar) {
|
||||
_ = db.Find("grammar", &g, "where tag LIKE '%"+tag+"%' ORDER BY RANDOM() limit 1")
|
||||
return
|
||||
}
|
||||
66
plugin/nihongo/nihongo.go
Normal file
66
plugin/nihongo/nihongo.go
Normal file
@@ -0,0 +1,66 @@
|
||||
// Package nihongo 日语学习
|
||||
package nihongo
|
||||
|
||||
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/img/text"
|
||||
log "github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("nihongo", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "日语学习\n- 日语语法[xxx](使用tag随机)",
|
||||
PublicDataFolder: "Nihongo",
|
||||
})
|
||||
|
||||
getdb := ctxext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
db.DBPath = engine.DataFolder() + "nihongo.db"
|
||||
_, err := engine.GetLazyData("nihongo.db", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
err = db.Open(time.Hour * 24)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
err = db.Create("grammar", &grammar{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
n, err := db.Count("grammar")
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
log.Infof("[nihongo]读取%d条语法", n)
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnRegex(`^日语语法\s?([0-9A-Za-zぁ-んァ-ヶ]{1,6})$`, getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
g := getRandomGrammarByTag(ctx.State["regex_matched"].([]string)[1])
|
||||
if g.ID == 0 {
|
||||
ctx.SendChain(message.Text("未能找到", ctx.State["regex_matched"].([]string)[1], "相关标签的语法"))
|
||||
return
|
||||
}
|
||||
data, err := text.RenderToBase64(g.string(), text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
if id := ctx.SendChain(message.Image("base64://" + binary.BytesToString(data))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控了"))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -23,10 +23,10 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/rate"
|
||||
|
||||
// 画图
|
||||
"github.com/Coloured-glaze/gg"
|
||||
"github.com/FloatTech/zbputils/file"
|
||||
"github.com/FloatTech/zbputils/img/text"
|
||||
"github.com/FloatTech/zbputils/img/writer"
|
||||
"github.com/fogleman/gg"
|
||||
)
|
||||
|
||||
//nolint: asciicheck
|
||||
@@ -123,7 +123,7 @@ func (sql *婚姻登记) 离婚休夫(gid, husband int64) error {
|
||||
gidstr := strconv.FormatInt(gid, 10)
|
||||
husbandstr := strconv.FormatInt(husband, 10)
|
||||
// 先判断用户是否存在
|
||||
err := sql.db.Del(gidstr, "where target = "+husbandstr)
|
||||
err := sql.db.Del(gidstr, "where user = "+husbandstr)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -135,10 +135,10 @@ func (sql *婚姻登记) 复婚(gid, uid, target int64, username, targetname str
|
||||
tagstr := strconv.FormatInt(target, 10)
|
||||
var info userinfo
|
||||
err := sql.db.Find(gidstr, &info, "where user = "+uidstr)
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
err = sql.db.Find(gidstr, &info, "where user = "+tagstr)
|
||||
}
|
||||
if err == nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
updatetime := time.Now().Format("2006/01/02")
|
||||
|
||||
@@ -143,8 +143,13 @@ func init() { // 插件主体
|
||||
} else {
|
||||
msg = append(msg, message.Text("也许是这个?"))
|
||||
}
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
msg = append(msg, message.Image(result.Header.Thumbnail))
|
||||
if err == nil {
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
msg = append(msg, message.Image(result.Header.Thumbnail))
|
||||
} else {
|
||||
msg = append(msg, message.Image(pic))
|
||||
}
|
||||
} else {
|
||||
msg = append(msg, message.Image(pic))
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/fogleman/gg"
|
||||
"github.com/Coloured-glaze/gg"
|
||||
"github.com/golang/freetype"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
14
plugin/tarot/README.md
Normal file
14
plugin/tarot/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ZeroBot-Plugin-Tarot
|
||||
|
||||
[ZeroBot QQ机器人](https://github.com/wdvxdr1123/ZeroBot)插件,玄学占卜抽塔罗牌!
|
||||
|
||||
## 触发方式
|
||||
|
||||
- [x] 抽[塔罗牌|大阿卡纳|小阿卡纳]
|
||||
- [x] 抽n张[塔罗牌|大阿卡纳|小阿卡纳]
|
||||
- [x] 解塔罗牌[牌名]
|
||||
- [x] [塔罗|大阿卡纳|小阿卡纳|混合]牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]
|
||||
|
||||
## 致谢
|
||||
|
||||
解牌来自[MinatoAquaCrews/nonebot_plugin_tarot](https://github.com/MinatoAquaCrews/nonebot_plugin_tarot),感谢[KafCoppelia](https://github.com/KafCoppelia)的收集与整理!
|
||||
@@ -37,21 +37,21 @@ type formation struct {
|
||||
}
|
||||
type cardSet = map[string]card
|
||||
|
||||
var cardMap = make(cardSet, 30)
|
||||
var infoMap = make(map[string]cardInfo, 30)
|
||||
var cardMap = make(cardSet, 80)
|
||||
var infoMap = make(map[string]cardInfo, 80)
|
||||
var formationMap = make(map[string]formation, 10)
|
||||
|
||||
// var cardName = make([]string, 30)
|
||||
// var formationName = make([]string, 10)
|
||||
var majorArcanaName = make([]string, 0, 80)
|
||||
var formationName = make([]string, 0, 10)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("tarot", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Help: "塔罗牌\n" +
|
||||
"- 抽塔罗牌\n" +
|
||||
"- 抽n张塔罗牌\n" +
|
||||
"- 抽[塔罗牌|大阿卡纳|小阿卡纳]\n" +
|
||||
"- 抽n张[塔罗牌|大阿卡纳|小阿卡纳]\n" +
|
||||
"- 解塔罗牌[牌名]\n" +
|
||||
"- 塔罗牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]",
|
||||
"- [塔罗|大阿卡纳|小阿卡纳|混合]牌阵[圣三角|时间之流|四要素|五牌阵|吉普赛十字|马蹄|六芒星]",
|
||||
PublicDataFolder: "Tarot",
|
||||
}).ApplySingle(ctxext.DefaultSingle)
|
||||
|
||||
@@ -67,12 +67,13 @@ func init() {
|
||||
return false
|
||||
}
|
||||
for _, card := range cardMap {
|
||||
infoMapKey := strings.Split(card.Name, "(")[0]
|
||||
infoMap[infoMapKey] = card.cardInfo
|
||||
// 可以拿来显示大阿尔卡纳列表
|
||||
// cardName = append(cardName, infoMapKey)
|
||||
infoMap[card.Name] = card.cardInfo
|
||||
}
|
||||
logrus.Infof("[tarot]读取%d张大阿尔卡纳塔罗牌", len(cardMap))
|
||||
for i := 0; i < 22; i++ {
|
||||
// 噢天哪,我应该把json里面序号设成int
|
||||
majorArcanaName = append(majorArcanaName, cardMap[strconv.Itoa(i)].Name)
|
||||
}
|
||||
logrus.Infof("[tarot]读取%d张塔罗牌", len(cardMap))
|
||||
formation, err := engine.GetLazyData("formation.json", true)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
@@ -83,15 +84,21 @@ func init() {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return false
|
||||
}
|
||||
for k := range formationMap {
|
||||
formationName = append(formationName, k)
|
||||
}
|
||||
logrus.Infof("[tarot]读取%d组塔罗牌阵", len(formationMap))
|
||||
return true
|
||||
})
|
||||
engine.OnRegex(`^抽(\d{1,2}张)?塔罗牌$`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
engine.OnRegex(`^抽(\d{1,2}张)?((塔罗牌|大阿(尔)?卡纳)|小阿(尔)?卡纳)$`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
match := ctx.State["regex_matched"].([]string)[1]
|
||||
cardType := ctx.State["regex_matched"].([]string)[2]
|
||||
n := 1
|
||||
reasons := [...]string{"您抽到的是~\n", "锵锵锵,塔罗牌的预言是~\n", "诶,让我看看您抽到了~\n"}
|
||||
reasons := [...]string{"您抽到的是~\n『", "锵锵锵,塔罗牌的预言是~\n『", "诶,让我看看您抽到了~\n『"}
|
||||
position := [...]string{"正位", "逆位"}
|
||||
reverse := [...]string{"", "Reverse"}
|
||||
start := 0
|
||||
length := 22
|
||||
if match != "" {
|
||||
var err error
|
||||
n, err = strconv.Atoi(match[:len(match)-3])
|
||||
@@ -112,14 +119,18 @@ func init() {
|
||||
return
|
||||
}
|
||||
}
|
||||
if strings.Contains(cardType, "小") {
|
||||
start = 22
|
||||
length = 55
|
||||
}
|
||||
if n == 1 {
|
||||
i := rand.Intn(22)
|
||||
i := rand.Intn(length) + start
|
||||
p := rand.Intn(2)
|
||||
card := cardMap[(strconv.Itoa(i))]
|
||||
card := cardMap[strconv.Itoa(i)]
|
||||
name := card.Name
|
||||
if id := ctx.SendChain(
|
||||
message.Text(reasons[rand.Intn(len(reasons))], position[p], " 的 ", name, "\n"),
|
||||
message.Image(fmt.Sprintf(bed+"MajorArcana%s/%d.png", reverse[p], i))); id.ID() == 0 {
|
||||
message.Text(reasons[rand.Intn(len(reasons))], position[p], "』的『", name, "』\n"),
|
||||
message.Image(fmt.Sprintf("%s/%s/%s", bed, reverse[p], card.ImgURL))); id.ID() == 0 {
|
||||
ctx.SendChain(message.Text("ERROR:可能被风控了"))
|
||||
}
|
||||
return
|
||||
@@ -127,19 +138,19 @@ func init() {
|
||||
msg := make([]message.MessageSegment, n)
|
||||
randomIntMap := make(map[int]int, 30)
|
||||
for i := range msg {
|
||||
j := rand.Intn(22)
|
||||
j := rand.Intn(length)
|
||||
_, ok := randomIntMap[j]
|
||||
for ok {
|
||||
j = rand.Intn(22)
|
||||
j = rand.Intn(length)
|
||||
_, ok = randomIntMap[j]
|
||||
}
|
||||
randomIntMap[j] = 0
|
||||
p := rand.Intn(2)
|
||||
card := cardMap[(strconv.Itoa(j))]
|
||||
card := cardMap[strconv.Itoa(j+start)]
|
||||
name := card.Name
|
||||
tarotMsg := []message.MessageSegment{
|
||||
message.Text(reasons[rand.Intn(len(reasons))], position[p], " 的 ", name, "\n"),
|
||||
message.Image(fmt.Sprintf(bed+"MajorArcana%s/%d.png", reverse[p], j))}
|
||||
message.Text(reasons[rand.Intn(len(reasons))], position[p], "』的『", name, "』\n"),
|
||||
message.Image(fmt.Sprintf("%s/%s/%s", bed, reverse[p], card.ImgURL))}
|
||||
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotMsg...)
|
||||
}
|
||||
ctx.SendGroupForwardMessage(ctx.Event.GroupID, msg)
|
||||
@@ -155,37 +166,69 @@ func init() {
|
||||
message.Text("\n正位:", info.Description),
|
||||
message.Text("\n逆位:", info.ReverseDescription))
|
||||
} else {
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~"))
|
||||
var build strings.Builder
|
||||
build.WriteString("塔罗牌列表\n大阿尔卡纳:\n")
|
||||
build.WriteString(strings.Join(majorArcanaName[:7], " "))
|
||||
build.WriteString("\n")
|
||||
build.WriteString(strings.Join(majorArcanaName[7:14], " "))
|
||||
build.WriteString("\n")
|
||||
build.WriteString(strings.Join(majorArcanaName[14:22], " "))
|
||||
build.WriteString("\n小阿尔卡纳:\n[圣杯|星币|宝剑|权杖] [0-10|侍从|骑士|王后|国王]")
|
||||
txt := build.String()
|
||||
cardList, err := text.RenderToBase64(txt, text.FontFile, 420, 20)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~"))
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
}
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~"), message.Image("base64://"+binary.BytesToString(cardList)))
|
||||
}
|
||||
})
|
||||
engine.OnRegex(`^塔罗牌阵\s?(.*)`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
match := ctx.State["regex_matched"].([]string)[1]
|
||||
engine.OnRegex(`^((塔罗|大阿(尔)?卡纳)|小阿(尔)?卡纳|混合)牌阵\s?(.*)`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
cardType := ctx.State["regex_matched"].([]string)[1]
|
||||
match := ctx.State["regex_matched"].([]string)[5]
|
||||
info, ok := formationMap[match]
|
||||
position := [...]string{"正位", "逆位"}
|
||||
reverse := [...]string{"", "Reverse"}
|
||||
start, length := 0, 22
|
||||
if strings.Contains(cardType, "小") {
|
||||
start = 22
|
||||
length = 55
|
||||
} else if cardType == "混合" {
|
||||
start = 0
|
||||
length = 77
|
||||
}
|
||||
if ok {
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
var build strings.Builder
|
||||
build.WriteString(ctx.CardOrNickName(ctx.Event.UserID))
|
||||
build.WriteString("---")
|
||||
build.WriteString(match)
|
||||
build.WriteString("\n")
|
||||
msg := make([]message.MessageSegment, info.CardsNum)
|
||||
msg := make([]message.MessageSegment, info.CardsNum+1)
|
||||
randomIntMap := make(map[int]int, 30)
|
||||
for i := range msg {
|
||||
j := rand.Intn(22)
|
||||
for i := 0; i < info.CardsNum; i++ {
|
||||
j := rand.Intn(length)
|
||||
_, ok := randomIntMap[j]
|
||||
for ok {
|
||||
j = rand.Intn(22)
|
||||
j = rand.Intn(length)
|
||||
_, ok = randomIntMap[j]
|
||||
}
|
||||
randomIntMap[j] = 0
|
||||
p := rand.Intn(2)
|
||||
card := cardMap[(strconv.Itoa(j))]
|
||||
card := cardMap[strconv.Itoa(j+start)]
|
||||
name := card.Name
|
||||
tarotMsg := []message.MessageSegment{message.Image(fmt.Sprintf(bed+"MajorArcana%s/%d.png", reverse[p], j))}
|
||||
description := card.Description
|
||||
if p == 1 {
|
||||
description = card.ReverseDescription
|
||||
}
|
||||
tarotMsg := []message.MessageSegment{message.Image(fmt.Sprintf("%s/%s/%s", bed, reverse[p], card.ImgURL))}
|
||||
build.WriteString(info.Represent[0][i])
|
||||
build.WriteString(": ")
|
||||
build.WriteString(":『")
|
||||
build.WriteString(position[p])
|
||||
build.WriteString(" 的 ")
|
||||
build.WriteString("』的『")
|
||||
build.WriteString(name)
|
||||
build.WriteString("』\n其释义为: \n")
|
||||
build.WriteString(description)
|
||||
build.WriteString("\n")
|
||||
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotMsg...)
|
||||
}
|
||||
@@ -195,11 +238,10 @@ func init() {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
// TODO 视gocq变化将牌阵信息加入转发列表中
|
||||
ctx.SendChain(message.Image("base64://" + binary.BytesToString(formation)))
|
||||
msg[info.CardsNum] = ctxext.FakeSenderForwardNode(ctx, []message.MessageSegment{message.Image("base64://" + binary.BytesToString(formation))}...)
|
||||
ctx.SendGroupForwardMessage(ctx.Event.GroupID, msg)
|
||||
} else {
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~"))
|
||||
ctx.SendChain(message.Text("没有找到", match, "噢~\n现有牌阵列表: \n", strings.Join(formationName, "\n")))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func init() { // 插件主体
|
||||
} else if len(result.Result) > 0 {
|
||||
r := result.Result[0]
|
||||
hint := "我有把握是这个!"
|
||||
if r.Similarity < 0.8 {
|
||||
if r.Similarity < 80 {
|
||||
hint = "大概是这个?"
|
||||
}
|
||||
mf := int(r.From / 60)
|
||||
|
||||
@@ -14,12 +14,12 @@ import (
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/tl"
|
||||
|
||||
"github.com/Coloured-glaze/gg"
|
||||
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/writer"
|
||||
"github.com/fogleman/gg"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/extension/single"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
@@ -64,7 +64,9 @@ func init() {
|
||||
DisableOnDefault: false,
|
||||
Help: "猜单词\n" +
|
||||
"- 个人猜单词\n" +
|
||||
"- 团队猜单词",
|
||||
"- 团队猜单词\n" +
|
||||
"- 团队六阶猜单词\n" +
|
||||
"- 团队七阶猜单词",
|
||||
PublicDataFolder: "Wordle",
|
||||
}).ApplySingle(single.New(
|
||||
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
|
||||
|
||||
Reference in New Issue
Block a user