mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2026-02-07 15:40:26 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b505d050a | ||
|
|
fc9a21d2d1 | ||
|
|
e84e44476a | ||
|
|
b012df4c23 | ||
|
|
08e02ab730 | ||
|
|
fb090839d6 | ||
|
|
ac2d53352c | ||
|
|
35292a69fc | ||
|
|
cd16a755d7 | ||
|
|
1e7b2d3335 | ||
|
|
20d49ccf15 | ||
|
|
1f66f47ce6 | ||
|
|
34f3b9ba2a | ||
|
|
2fa7868838 | ||
|
|
b6ddda1d51 | ||
|
|
a1621f34a0 | ||
|
|
21aa3bc49f | ||
|
|
617d4f50a4 | ||
|
|
cb0ffa0c17 | ||
|
|
0615993297 | ||
|
|
1c0d91424a | ||
|
|
c94ee365ce | ||
|
|
19e5e6636f | ||
|
|
cac3a4be81 | ||
|
|
beada7f4da | ||
|
|
43b45ce6c5 | ||
|
|
95dd5e6b94 | ||
|
|
566f6ecfd5 | ||
|
|
5b28ad75b7 | ||
|
|
997857a558 | ||
|
|
4269057283 | ||
|
|
f70cab80c2 | ||
|
|
609d819610 |
67
README.md
67
README.md
@@ -192,6 +192,18 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]
|
||||
|
||||
- [x] 早安 | 晚安
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>违禁词检测</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/antiabuse"
|
||||
`
|
||||
- [x] 添加违禁词
|
||||
|
||||
- [x] 删除违禁词
|
||||
|
||||
- [x] 查看违禁词
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>ATRI</summary>
|
||||
@@ -255,6 +267,8 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]
|
||||
- [x] 翻牌
|
||||
|
||||
- [x] 赞我
|
||||
|
||||
- [x] 群签到
|
||||
|
||||
- [x] [开启 | 关闭]入群验证
|
||||
|
||||
@@ -276,6 +290,20 @@ zerobot [-h] [-m] [-n nickname] [-t token] [-u url] [-g url] [-p prefix] [-d|w]
|
||||
|
||||
- 设置欢迎语可选添加参数说明:{at}可在发送时艾特被欢迎者 {nickname}是被欢迎者名字 {avatar}是被欢迎者头像 {uid}是被欢迎者QQ号 {gid}是当前群群号 {groupname} 是当前群群名
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>群应用:AI声聊</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/airecord"`
|
||||
|
||||
- [x] 设置AI语音群号1048452984(tips:机器人任意所在群聊即可)
|
||||
|
||||
- [x] 设置AI语音模型
|
||||
|
||||
- [x] 查看AI语音配置
|
||||
|
||||
- [x] 发送AI语音xxx
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>定时指令触发器</summary>
|
||||
@@ -384,6 +412,18 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 设置默认限速为每 m [分钟 | 秒] n 次触发
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>aiimage</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiimage"`
|
||||
|
||||
- [x] 设置AI画图密钥xxx
|
||||
- [x] 设置AI画图接口地址https://api.siliconflow.cn/v1/images/generations
|
||||
- [x] 设置AI画图模型名Kwai-Kolors/Kolors
|
||||
- [x] 查看AI画图配置
|
||||
- [x] AI画图 [描述]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>AIWife</summary>
|
||||
@@ -605,6 +645,17 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
- [x] 磕cp大老师 雪乃
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>奇怪语言加解密</summary>
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/crypter"`
|
||||
|
||||
- [x] 齁语加密 [文本] 或 h加密 [文本]
|
||||
- [x] 齁语解密 [密文] 或 h解密 [密文]
|
||||
- [x] fumo加密 [文本]
|
||||
- [x] fumo解密 [文本]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>今日早报</summary>
|
||||
@@ -1026,6 +1077,10 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
- [x] 酷我点歌[xxx]
|
||||
|
||||
- [x] 酷狗点歌[xxx]
|
||||
|
||||
- [x] qq点歌[xxx]
|
||||
|
||||
- [x] 咪咕点歌[xxx]
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -1464,7 +1519,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count"`
|
||||
|
||||
- [x] 热词 [群号] [消息数目]|热词 123456 1000
|
||||
- [x] 热词 [消息数目]|热词 1000
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@@ -1580,14 +1635,20 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
- [x] 设置AI聊天温度80
|
||||
- [x] 设置AI聊天接口类型[OpenAI|OLLaMA|GenAI]
|
||||
- [x] 设置AI聊天(不)支持系统提示词
|
||||
- [x] 设置AI聊天接口地址https://xxx
|
||||
- [x] 设置AI聊天接口地址https://api.siliconflow.cn/v1/chat/completions
|
||||
- [x] 设置AI聊天密钥xxx
|
||||
- [x] 设置AI聊天模型名xxx
|
||||
- [x] 设置AI聊天模型名Qwen/Qwen3-8B
|
||||
- [x] 查看AI聊天系统提示词
|
||||
- [x] 重置AI聊天系统提示词
|
||||
- [x] 设置AI聊天系统提示词xxx
|
||||
- [x] 设置AI聊天分隔符`</think>`(留空则清除)
|
||||
- [x] 设置AI聊天(不)响应AT
|
||||
- [x] 设置AI聊天最大长度4096
|
||||
- [x] 设置AI聊天TopP 0.9
|
||||
- [x] 设置AI聊天(不)以AI语音输出
|
||||
- [x] 查看AI聊天配置
|
||||
- [x] 重置AI聊天
|
||||
- [x] 群聊总结 [消息数目]|群聊总结 1000
|
||||
|
||||
</details>
|
||||
<details>
|
||||
|
||||
@@ -38,12 +38,18 @@ func setConsoleTitle(title string) (err error) {
|
||||
}
|
||||
|
||||
func init() {
|
||||
debugMode := os.Getenv("DEBUG_MODE") == "1"
|
||||
stdin := windows.Handle(os.Stdin.Fd())
|
||||
|
||||
var mode uint32
|
||||
err := windows.GetConsoleMode(stdin, &mode)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if debugMode {
|
||||
logrus.Warnf("调试模式下忽略控制台模式获取失败: %v", err)
|
||||
return // 调试模式下直接返回,跳过后续配置
|
||||
} else {
|
||||
panic(err) // 非调试模式下 panic
|
||||
}
|
||||
}
|
||||
|
||||
mode &^= windows.ENABLE_QUICK_EDIT_MODE // 禁用快速编辑模式
|
||||
|
||||
2
data
2
data
Submodule data updated: ca3652920a...6bcac0faab
8
go.mod
8
go.mod
@@ -4,7 +4,7 @@ go 1.20
|
||||
|
||||
require (
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250423082452-e16339a3962c
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250901143505-180d33844860
|
||||
github.com/FloatTech/floatbox v0.0.0-20250513111443-adba80e84e80
|
||||
github.com/FloatTech/gg v1.1.3
|
||||
github.com/FloatTech/imgfactory v0.2.2-0.20230413152719-e101cc3606ef
|
||||
@@ -12,7 +12,7 @@ require (
|
||||
github.com/FloatTech/sqlite v1.7.1
|
||||
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562
|
||||
github.com/FloatTech/zbpctrl v1.7.0
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250330125231-d8be1c9d3b9c
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250812085410-2741050f465f
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7
|
||||
github.com/RomiChan/websocket v1.4.3-0.20220227141055-9b2c6168c9c5
|
||||
github.com/Tnze/go-mc v1.20.2
|
||||
@@ -22,7 +22,7 @@ require (
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/fumiama/ahsai v0.1.0
|
||||
github.com/fumiama/cron v1.3.0
|
||||
github.com/fumiama/deepinfra v0.0.0-20250330125128-71ec2f7c085e
|
||||
github.com/fumiama/deepinfra v0.0.0-20250910022828-8cde75e137f4
|
||||
github.com/fumiama/go-base16384 v1.7.0
|
||||
github.com/fumiama/go-registry v0.2.7
|
||||
github.com/fumiama/gotracemoe v0.0.3
|
||||
@@ -45,7 +45,7 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250330133859-27c25d9412b5
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250804063440-ccc03e33ac20
|
||||
gitlab.com/gomidi/midi/v2 v2.1.7
|
||||
golang.org/x/image v0.24.0
|
||||
golang.org/x/sys v0.30.0
|
||||
|
||||
18
go.sum
18
go.sum
@@ -1,8 +1,8 @@
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1 h1:RQsAmgDSAkiq22I6n7XJ2t3afgzFeqjY46FGhvrx4cw=
|
||||
github.com/Baidu-AIP/golang-sdk v1.1.1/go.mod h1:bXnGw7xPeKt8aF7UCELKrV6UZ/46spItONK1RQBQj1Y=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250423082452-e16339a3962c h1:bEe8VP2aHLR2NHk1BsBQFtP0XE3cxquvr0tW0CkKcDk=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250423082452-e16339a3962c/go.mod h1:XXG1eBJf+eeWacQx5azsQKL5Gg7jDYTFyyZGIa/56js=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250901143505-180d33844860 h1:ddthsMzYC2LZ517/71W//9VsXT82CSBALVt3sQY5vfA=
|
||||
github.com/FloatTech/AnimeAPI v1.7.1-0.20250901143505-180d33844860/go.mod h1:CzpSeo5Pvslnq7Ho14E438Yn/flFMKzjGeX2nbC1mzk=
|
||||
github.com/FloatTech/floatbox v0.0.0-20250513111443-adba80e84e80 h1:lFD1pd8NkYCrw0QpTX/T5pJ67I7AL5eGxQ4v0r9f81Q=
|
||||
github.com/FloatTech/floatbox v0.0.0-20250513111443-adba80e84e80/go.mod h1:IWoFFqu+0FeaHHQdddyiTRL5z7gJME6qHC96qh0R2sc=
|
||||
github.com/FloatTech/gg v1.1.3 h1:+GlL02lTKsxJQr4WCuNwVxC1/eBZrCvypCIBtxuOFb4=
|
||||
@@ -17,8 +17,8 @@ github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562 h1:snfw7FNFym1eNnLrQ
|
||||
github.com/FloatTech/ttl v0.0.0-20240716161252-965925764562/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/FloatTech/zbpctrl v1.7.0 h1:Hxo6EIhJo+pHjcQP9QgIJgluaT1pHH99zkk3njqTNMo=
|
||||
github.com/FloatTech/zbpctrl v1.7.0/go.mod h1:xmM4dSwHA02Gei3ogCRiG+RTrw/7Z69PfrN5NYf8BPE=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250330125231-d8be1c9d3b9c h1:nIybmanPvQknseVOJ+s4/m3q7EZxtqMoTy3wiiZts6E=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250330125231-d8be1c9d3b9c/go.mod h1:ArZ0fMAcmPEIXOqDmfzbSx+oYH8sssApQnbCu694iS8=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250812085410-2741050f465f h1:5jnrFe9FTydb/pcUhxkWHuQVCwmYIZmneOkvmgHOwGI=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250812085410-2741050f465f/go.mod h1:HG/yZwExV3b1Vqu4chbqwhfX4hx7gDS07QO436JkwIg=
|
||||
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7 h1:S/ferNiehVjNaBMNNBxUjLtVmP/YWD6Yh79RfPv4ehU=
|
||||
github.com/RomiChan/syncx v0.0.0-20240418144900-b7402ffdebc7/go.mod h1:vD7Ra3Q9onRtojoY5sMCLQ7JBgjUsrXDnDKyFxqpf9w=
|
||||
@@ -59,8 +59,8 @@ github.com/fumiama/ahsai v0.1.0 h1:LXD61Kaj6kJHa3AEGsLIfKNzcgaVxg7JB72OR4yNNZ4=
|
||||
github.com/fumiama/ahsai v0.1.0/go.mod h1:fFeNnqgo44i8FIaguK659aQryuZeFy+4klYLQu/rfdk=
|
||||
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/deepinfra v0.0.0-20250330125128-71ec2f7c085e h1:L/Z5N6UfpuqNIiPUrjSzmrnWj3mjd3auwl/2ctpGXNY=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250330125128-71ec2f7c085e/go.mod h1:wW05PQSn8mo1mZIoa6LBUE+3xIBjkoONvnfPTV5ZOhY=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250910022828-8cde75e137f4 h1:cV3HXXLNudIL9rIEYt1RCgl6H4703nE3+jL4pJNsRtc=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250910022828-8cde75e137f4/go.mod h1:wW05PQSn8mo1mZIoa6LBUE+3xIBjkoONvnfPTV5ZOhY=
|
||||
github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
|
||||
github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnOHeNs=
|
||||
@@ -124,8 +124,8 @@ github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
|
||||
github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jozsefsallai/gophersauce v1.0.1 h1:BA3ovtQRrAb1qYU9JoRLbDHpxnDunlNcEkEfhCvDDCM=
|
||||
github.com/jozsefsallai/gophersauce v1.0.1/go.mod h1:YVEI7djliMTmZ1Vh01YPF8bUHi+oKhe3yXgKf1T49vg=
|
||||
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
|
||||
@@ -199,8 +199,8 @@ github.com/vcaesar/cedar v0.20.2/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFe
|
||||
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2 h1:Y17/oYNuXwZg6TFag06qe8sBajwwsuvPiJJXcUcLL6E=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2/go.mod h1:Zi4hbaqlWpYajnXB2K22IUYVXRXaLfSGNNR7P4ukyyQ=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250330133859-27c25d9412b5 h1:HsMcBsVpYuQv+W8pjX5WdwYROrFQP9c5Pbf4x4adDus=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250330133859-27c25d9412b5/go.mod h1:C86nQ0gIdAri4K2vg8IIQIslt08zzrKMcqYt8zhkx1M=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250804063440-ccc03e33ac20 h1:Yzd+cbiJQYtf6cZDP5ZB/LqjNWiV752+5P6Eua+wnic=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250804063440-ccc03e33ac20/go.mod h1:C86nQ0gIdAri4K2vg8IIQIslt08zzrKMcqYt8zhkx1M=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
|
||||
@@ -5,8 +5,8 @@ schema = 3
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-hKshA0K92bKuK92mmtM0osVmqLJcSbeobeWSDpQoRCo="
|
||||
[mod."github.com/FloatTech/AnimeAPI"]
|
||||
version = "v1.7.1-0.20250423082452-e16339a3962c"
|
||||
hash = "sha256-pyv242GLfslHEgQN5TgYsdpJKWzWR8qc4mJ8NXF0I8s="
|
||||
version = "v1.7.1-0.20250901143505-180d33844860"
|
||||
hash = "sha256-k1MlgaBGwpaqoVk+8WYfXoVLfzqyEQW5LQaJgBKlhUA="
|
||||
[mod."github.com/FloatTech/floatbox"]
|
||||
version = "v0.0.0-20250513111443-adba80e84e80"
|
||||
hash = "sha256-Zt9zkUa3qqldrSttAq66YLPZPxrnkOR2MaU7oapIWEE="
|
||||
@@ -29,8 +29,8 @@ schema = 3
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-HDDnE0oktWJH1tkxuQwUUbeJhmVwY5fyc/vR72D2mkU="
|
||||
[mod."github.com/FloatTech/zbputils"]
|
||||
version = "v1.7.2-0.20250330125231-d8be1c9d3b9c"
|
||||
hash = "sha256-v2ueCLEwy6oqF8HoxodDHcNOeDPzKn+7fiYSC2L2/e4="
|
||||
version = "v1.7.2-0.20250812085410-2741050f465f"
|
||||
hash = "sha256-NoCU7tqzihm2xEr1LelrfMzeg9RDQ9OsFBVXfNDcxvs="
|
||||
[mod."github.com/RomiChan/syncx"]
|
||||
version = "v0.0.0-20240418144900-b7402ffdebc7"
|
||||
hash = "sha256-L1j1vgiwqXpF9pjMoRRlrQUHzoULisw/01plaEAwxs4="
|
||||
@@ -77,8 +77,8 @@ schema = 3
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-/sN7X8dKXQgv8J+EDzVUB+o+AY9gBC8e1C6sYhaTy1k="
|
||||
[mod."github.com/fumiama/deepinfra"]
|
||||
version = "v0.0.0-20250330125128-71ec2f7c085e"
|
||||
hash = "sha256-O7Om4mIcBB2zdVxaeKh7qHbiG83gVLP+f8vxSF17kCw="
|
||||
version = "v0.0.0-20250910022828-8cde75e137f4"
|
||||
hash = "sha256-1CV8t3R91maqJztHg7whECqvS4+sxWcSvq+EyO4PyZ8="
|
||||
[mod."github.com/fumiama/go-base16384"]
|
||||
version = "v1.7.0"
|
||||
hash = "sha256-vTAsBBYe2ISzb2Nba5E96unodZSkhMcqo6hbwR01nz8="
|
||||
@@ -227,8 +227,8 @@ schema = 3
|
||||
version = "v2.1.2"
|
||||
hash = "sha256-GXWWea/u6BezTsPPrWhTYiTetPP/YW6P+Sj4YdocPaM="
|
||||
[mod."github.com/wdvxdr1123/ZeroBot"]
|
||||
version = "v1.8.2-0.20250330133859-27c25d9412b5"
|
||||
hash = "sha256-gT3uFTg5E0Th3r1M1vLzr0QtOjbMusqEjD/ckoBdDFc="
|
||||
version = "v1.8.2-0.20250804063440-ccc03e33ac20"
|
||||
hash = "sha256-2bFcPmcDsZxTD3sU3i2QD4M/ehSF43Ohf5ltuq1QtOQ="
|
||||
[mod."github.com/yusufpapurcu/wmi"]
|
||||
version = "v1.2.4"
|
||||
hash = "sha256-N+YDBjOW59YOsZ2lRBVtFsEEi48KhNQRb63/0ZSU3bA="
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
package banner
|
||||
|
||||
// Version ...
|
||||
var Version = "v1.9.7"
|
||||
var Version = "v1.9.9"
|
||||
|
||||
// Copyright ...
|
||||
var Copyright = "© 2020 - 2025 FloatTech"
|
||||
|
||||
// Banner ...
|
||||
var Banner = "* OneBot + ZeroBot + Golang\n" +
|
||||
"* Version " + Version + " - 2025-05-14 21:52:40 +0900 JST\n" +
|
||||
"* Version " + Version + " - 2025-09-10 10:40:39 +0800 CST\n" +
|
||||
"* Copyright " + Copyright + ". All Rights Reserved.\n" +
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
|
||||
|
||||
@@ -27,7 +27,7 @@ var Banner = "* OneBot + ZeroBot + Golang\n" +
|
||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
|
||||
`
|
||||
|
||||
const timeformat = `2006-01-02 15:04:05 +0900 JST`
|
||||
const timeformat = `2006-01-02 15:04:05 +0800 CST`
|
||||
|
||||
func main() {
|
||||
f, err := os.Create("banner/banner.go")
|
||||
|
||||
4
main.go
4
main.go
@@ -38,6 +38,8 @@ import (
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/sleepmanage" // 统计睡眠时间
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/airecord" // 群应用:AI声聊
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/atri" // ATRI词库
|
||||
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/manager" // 群管
|
||||
@@ -65,6 +67,7 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/custom" // 自定义插件合集
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/ahsai" // ahsai tts
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aifalse" // 服务器监控
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiimage" // AI画图
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aiwife" // 随机老婆
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/animetrace" // AnimeTrace 动画/Galgame识别
|
||||
@@ -81,6 +84,7 @@ import (
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/chrev" // 英文字符翻转
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/coser" // 三次元小姐姐
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/cpstory" // cp短打
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/crypter" // 奇怪语言加解密
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/dailynews" // 今日早报
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/danbooru" // DeepDanbooru二次元图标签识别
|
||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/diana" // 嘉心糖发病
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
@@ -12,17 +14,22 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var cfg = newconfig()
|
||||
var (
|
||||
cfg = newconfig()
|
||||
)
|
||||
|
||||
type config struct {
|
||||
ModelName string
|
||||
Type int
|
||||
MaxN uint
|
||||
TopP float32
|
||||
SystemP string
|
||||
API string
|
||||
Key string
|
||||
Separator string
|
||||
NoReplyAT bool
|
||||
NoSystemP bool
|
||||
NoRecord bool
|
||||
}
|
||||
|
||||
func newconfig() config {
|
||||
@@ -94,3 +101,98 @@ func newextrasetbool(ptr *bool) func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetuint(ptr *uint) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseUint(args, 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = uint(n)
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func newextrasetfloat32(ptr *float32) func(ctx *zero.Ctx) {
|
||||
return func(ctx *zero.Ctx) {
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if args == "" {
|
||||
ctx.SendChain(message.Text("ERROR: empty args"))
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
n, err := strconv.ParseFloat(args, 32)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse args err: ", err))
|
||||
return
|
||||
}
|
||||
*ptr = float32(n)
|
||||
err = c.SetExtra(&cfg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set extra err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
}
|
||||
}
|
||||
|
||||
func printConfig(rate int64, temperature int64, cfg config) string {
|
||||
maxn := cfg.MaxN
|
||||
if maxn == 0 {
|
||||
maxn = 4096
|
||||
}
|
||||
topp := cfg.TopP
|
||||
if topp == 0 {
|
||||
topp = 0.9
|
||||
}
|
||||
var builder strings.Builder
|
||||
builder.WriteString("当前AI聊天配置:\n")
|
||||
builder.WriteString(fmt.Sprintf("• 模型名:%s\n", cfg.ModelName))
|
||||
builder.WriteString(fmt.Sprintf("• 接口类型:%d(%s)\n", cfg.Type, apilist[cfg.Type]))
|
||||
builder.WriteString(fmt.Sprintf("• 触发概率:%d%%\n", rate))
|
||||
builder.WriteString(fmt.Sprintf("• 温度:%.2f\n", float32(temperature)/100))
|
||||
builder.WriteString(fmt.Sprintf("• 最大长度:%d\n", maxn))
|
||||
builder.WriteString(fmt.Sprintf("• TopP:%.1f\n", topp))
|
||||
builder.WriteString(fmt.Sprintf("• 系统提示词:%s\n", cfg.SystemP))
|
||||
builder.WriteString(fmt.Sprintf("• 接口地址:%s\n", cfg.API))
|
||||
builder.WriteString(fmt.Sprintf("• 密钥:%s\n", maskKey(cfg.Key)))
|
||||
builder.WriteString(fmt.Sprintf("• 分隔符:%s\n", cfg.Separator))
|
||||
builder.WriteString(fmt.Sprintf("• 响应@:%s\n", yesNo(!cfg.NoReplyAT)))
|
||||
builder.WriteString(fmt.Sprintf("• 支持系统提示词:%s\n", yesNo(!cfg.NoSystemP)))
|
||||
builder.WriteString(fmt.Sprintf("• 以AI语音输出:%s\n", yesNo(!cfg.NoRecord)))
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func maskKey(key string) string {
|
||||
if len(key) <= 4 {
|
||||
return "****"
|
||||
}
|
||||
return key[:2] + strings.Repeat("*", len(key)-4) + key[len(key)-2:]
|
||||
}
|
||||
|
||||
func yesNo(b bool) string {
|
||||
if b {
|
||||
return "是"
|
||||
}
|
||||
return "否"
|
||||
}
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
// Package aichat OpenAI聊天
|
||||
// Package aichat OpenAI聊天和群聊总结
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fumiama/deepinfra"
|
||||
"github.com/fumiama/deepinfra/model"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/airecord"
|
||||
"github.com/FloatTech/floatbox/process"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -29,22 +34,60 @@ var (
|
||||
"- 设置AI聊天温度80\n" +
|
||||
"- 设置AI聊天接口类型[OpenAI|OLLaMA|GenAI]\n" +
|
||||
"- 设置AI聊天(不)支持系统提示词\n" +
|
||||
"- 设置AI聊天接口地址https://xxx\n" +
|
||||
"- 设置AI聊天接口地址https://api.siliconflow.cn/v1/chat/completions\n" +
|
||||
"- 设置AI聊天密钥xxx\n" +
|
||||
"- 设置AI聊天模型名xxx\n" +
|
||||
"- 设置AI聊天模型名Qwen/Qwen3-8B\n" +
|
||||
"- 查看AI聊天系统提示词\n" +
|
||||
"- 重置AI聊天系统提示词\n" +
|
||||
"- 设置AI聊天系统提示词xxx\n" +
|
||||
"- 设置AI聊天分隔符</think>(留空则清除)\n" +
|
||||
"- 设置AI聊天(不)响应AT",
|
||||
"- 设置AI聊天(不)响应AT\n" +
|
||||
"- 设置AI聊天最大长度4096\n" +
|
||||
"- 设置AI聊天TopP 0.9\n" +
|
||||
"- 设置AI聊天(不)以AI语音输出\n" +
|
||||
"- 查看AI聊天配置\n" +
|
||||
"- 重置AI聊天\n" +
|
||||
"- 群聊总结 [消息数目]|群聊总结 1000\n" +
|
||||
"- /gpt [内容] (使用大模型聊天)\n",
|
||||
|
||||
PrivateDataFolder: "aichat",
|
||||
})
|
||||
)
|
||||
|
||||
var apitypes = map[string]uint8{
|
||||
"OpenAI": 0,
|
||||
"OLLaMA": 1,
|
||||
"GenAI": 2,
|
||||
var (
|
||||
apitypes = map[string]uint8{
|
||||
"OpenAI": 0,
|
||||
"OLLaMA": 1,
|
||||
"GenAI": 2,
|
||||
}
|
||||
apilist = [3]string{"OpenAI", "OLLaMA", "GenAI"}
|
||||
limit = ctxext.NewLimiterManager(time.Second*30, 1)
|
||||
)
|
||||
|
||||
// getModelParams 获取模型参数:温度(float32(temp)/100)、TopP和最大长度
|
||||
func getModelParams(temp int64) (temperature float32, topp float32, maxn uint) {
|
||||
// 处理温度参数
|
||||
if temp <= 0 {
|
||||
temp = 70 // default setting
|
||||
}
|
||||
if temp > 100 {
|
||||
temp = 100
|
||||
}
|
||||
temperature = float32(temp) / 100
|
||||
|
||||
// 处理TopP参数
|
||||
topp = cfg.TopP
|
||||
if topp == 0 {
|
||||
topp = 0.9
|
||||
}
|
||||
|
||||
// 处理最大长度参数
|
||||
maxn = cfg.MaxN
|
||||
if maxn == 0 {
|
||||
maxn = 4096
|
||||
}
|
||||
|
||||
return temperature, topp, maxn
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -74,31 +117,27 @@ func init() {
|
||||
return
|
||||
}
|
||||
|
||||
if temp <= 0 {
|
||||
temp = 70 // default setting
|
||||
}
|
||||
if temp > 100 {
|
||||
temp = 100
|
||||
}
|
||||
temperature, topp, maxn := getModelParams(temp)
|
||||
|
||||
x := deepinfra.NewAPI(cfg.API, cfg.Key)
|
||||
var mod model.Protocol
|
||||
|
||||
switch cfg.Type {
|
||||
case 0:
|
||||
mod = model.NewOpenAI(
|
||||
cfg.ModelName, cfg.Separator,
|
||||
float32(temp)/100, 0.9, 4096,
|
||||
)
|
||||
temperature, topp, maxn,
|
||||
).SetExtra(&map[string]bool{
|
||||
"enable_thinking": false,
|
||||
})
|
||||
case 1:
|
||||
mod = model.NewOLLaMA(
|
||||
cfg.ModelName, cfg.Separator,
|
||||
float32(temp)/100, 0.9, 4096,
|
||||
temperature, topp, maxn,
|
||||
)
|
||||
case 2:
|
||||
mod = model.NewGenAI(
|
||||
cfg.ModelName,
|
||||
float32(temp)/100, 0.9, 4096,
|
||||
temperature, topp, maxn,
|
||||
)
|
||||
default:
|
||||
logrus.Warnln("[aichat] unsupported AI type", cfg.Type)
|
||||
@@ -125,10 +164,20 @@ func init() {
|
||||
if t == "" {
|
||||
continue
|
||||
}
|
||||
if id != nil {
|
||||
id = ctx.SendChain(message.Reply(id), message.Text(t))
|
||||
logrus.Infoln("[aichat] 回复内容:", t)
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ""
|
||||
if !cfg.NoRecord {
|
||||
record = ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, t)
|
||||
}
|
||||
if record != "" {
|
||||
ctx.SendChain(message.Record(record))
|
||||
} else {
|
||||
id = ctx.SendChain(message.Text(t))
|
||||
if id != nil {
|
||||
id = ctx.SendChain(message.Reply(id), message.Text(t))
|
||||
} else {
|
||||
id = ctx.SendChain(message.Text(t))
|
||||
}
|
||||
}
|
||||
process.SleepAbout1sTo2s()
|
||||
}
|
||||
@@ -255,4 +304,242 @@ func init() {
|
||||
Handle(newextrasetbool(&cfg.NoReplyAT))
|
||||
en.OnRegex("^设置AI聊天(不)?支持系统提示词$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetbool(&cfg.NoSystemP))
|
||||
en.OnPrefix("设置AI聊天最大长度", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetuint(&cfg.MaxN))
|
||||
en.OnPrefix("设置AI聊天TopP", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetfloat32(&cfg.TopP))
|
||||
en.OnRegex("^设置AI聊天(不)?以AI语音输出$", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(newextrasetbool(&cfg.NoRecord))
|
||||
en.OnFullMatch("查看AI聊天配置", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("ERROR: no such plugin"))
|
||||
return
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
rate := c.GetData(gid) & 0xff
|
||||
temp := (c.GetData(gid) >> 8) & 0xff
|
||||
if temp <= 0 {
|
||||
temp = 70 // default setting
|
||||
}
|
||||
if temp > 100 {
|
||||
temp = 100
|
||||
}
|
||||
ctx.SendChain(message.Text(printConfig(rate, temp, cfg)))
|
||||
})
|
||||
en.OnFullMatch("重置AI聊天", ensureconfig, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
chat.Reset()
|
||||
ctx.SendChain(message.Text("成功"))
|
||||
})
|
||||
|
||||
// 添加群聊总结功能
|
||||
en.OnRegex(`^群聊总结\s?(\d*)$`, ensureconfig, zero.OnlyGroup, zero.AdminPermission).SetBlock(true).Limit(limit.LimitByGroup).Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女思考中..."))
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
rate := c.GetData(gid)
|
||||
temp := (rate >> 8) & 0xff
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if p > 1000 {
|
||||
p = 1000
|
||||
}
|
||||
if p == 0 {
|
||||
p = 200
|
||||
}
|
||||
group := ctx.GetGroupInfo(gid, false)
|
||||
if group.MemberCount == 0 {
|
||||
ctx.SendChain(message.Text(zero.BotConfig.NickName[0], "未加入", group.Name, "(", gid, "),无法获取总结"))
|
||||
return
|
||||
}
|
||||
|
||||
var messages []string
|
||||
|
||||
h := ctx.GetGroupMessageHistory(gid, 0, p, false)
|
||||
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
|
||||
nickname := msgObj.Get("sender.nickname").Str
|
||||
text := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
|
||||
if text != "" {
|
||||
messages = append(messages, nickname+": "+text)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(messages) == 0 {
|
||||
ctx.SendChain(message.Text("ERROR: 历史消息为空或者无法获得历史消息"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构造总结请求提示
|
||||
summaryPrompt := "请总结这个群聊内容,要求按发言顺序梳理,明确标注每个发言者的昵称,并完整呈现其核心观点、提出的问题、发表的看法或做出的回应,确保不遗漏关键信息,且能体现成员间的对话逻辑和互动关系:\n" +
|
||||
strings.Join(messages, "\n")
|
||||
|
||||
// 调用大模型API进行总结
|
||||
summary, err := llmchat(summaryPrompt, temp)
|
||||
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("群 ")
|
||||
b.WriteString(group.Name)
|
||||
b.WriteByte('(')
|
||||
b.WriteString(strconv.FormatInt(gid, 10))
|
||||
b.WriteString(") 的 ")
|
||||
b.WriteString(strconv.FormatInt(p, 10))
|
||||
b.WriteString(" 条消息总结:\n\n")
|
||||
b.WriteString(summary)
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
summaryText := b.String()
|
||||
msg := make(message.Message, 0)
|
||||
for len(summaryText) > 0 {
|
||||
if len(summaryText) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(summaryText)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := summaryText[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = summaryText[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
summaryText = summaryText[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
|
||||
// 添加 /gpt 命令处理(同时支持回复消息和直接使用)
|
||||
en.OnKeyword("/gpt", ensureconfig).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid == 0 {
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
rate := c.GetData(gid)
|
||||
temp := (rate >> 8) & 0xff
|
||||
text := ctx.MessageString()
|
||||
|
||||
var query string
|
||||
var replyContent string
|
||||
|
||||
// 检查是否是回复消息 (使用MessageElement检查而不是CQ码)
|
||||
for _, elem := range ctx.Event.Message {
|
||||
if elem.Type == "reply" {
|
||||
// 提取被回复的消息ID
|
||||
replyIDStr := elem.Data["id"]
|
||||
replyID, err := strconv.ParseInt(replyIDStr, 10, 64)
|
||||
if err == nil {
|
||||
// 获取被回复的消息内容
|
||||
replyMsg := ctx.GetMessage(replyID)
|
||||
if replyMsg.Elements != nil {
|
||||
replyContent = replyMsg.Elements.ExtractPlainText()
|
||||
}
|
||||
}
|
||||
break // 找到回复元素后退出循环
|
||||
}
|
||||
}
|
||||
|
||||
// 提取 /gpt 后面的内容
|
||||
parts := strings.SplitN(text, "/gpt", 2)
|
||||
|
||||
var gContent string
|
||||
if len(parts) > 1 {
|
||||
gContent = strings.TrimSpace(parts[1])
|
||||
}
|
||||
|
||||
// 组合内容:优先使用回复内容,如果同时有/gpt内容则拼接
|
||||
switch {
|
||||
case replyContent != "" && gContent != "":
|
||||
query = replyContent + "\n" + gContent
|
||||
case replyContent != "":
|
||||
query = replyContent
|
||||
case gContent != "":
|
||||
query = gContent
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
// 调用大模型API进行聊天
|
||||
reply, err := llmchat(query, temp)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 分割总结内容为多段(按1000字符长度切割)
|
||||
msg := make(message.Message, 0)
|
||||
for len(reply) > 0 {
|
||||
if len(reply) <= 1000 {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(reply)))
|
||||
break
|
||||
}
|
||||
|
||||
// 查找1000字符内的最后一个换行符,尽量在换行处分割
|
||||
chunk := reply[:1000]
|
||||
lastNewline := strings.LastIndex(chunk, "\n")
|
||||
if lastNewline > 0 {
|
||||
chunk = reply[:lastNewline+1]
|
||||
}
|
||||
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(chunk)))
|
||||
reply = reply[len(chunk):]
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// llmchat 调用大模型API包装
|
||||
func llmchat(prompt string, temp int64) (string, error) {
|
||||
temperature, topp, maxn := getModelParams(temp) // 使用默认温度70
|
||||
|
||||
x := deepinfra.NewAPI(cfg.API, cfg.Key)
|
||||
var mod model.Protocol
|
||||
switch cfg.Type {
|
||||
case 0:
|
||||
mod = model.NewOpenAI(
|
||||
cfg.ModelName, cfg.Separator,
|
||||
temperature, topp, maxn,
|
||||
).SetExtra(&map[string]bool{
|
||||
"enable_thinking": false,
|
||||
})
|
||||
case 1:
|
||||
mod = model.NewOLLaMA(
|
||||
cfg.ModelName, cfg.Separator,
|
||||
temperature, topp, maxn,
|
||||
)
|
||||
case 2:
|
||||
mod = model.NewGenAI(
|
||||
cfg.ModelName,
|
||||
temperature, topp, maxn,
|
||||
)
|
||||
default:
|
||||
logrus.Warnln("[aichat] unsupported AI type", cfg.Type)
|
||||
return "", errors.New("不支持的AI类型")
|
||||
}
|
||||
|
||||
data, err := x.Request(mod.User(prompt))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return strings.TrimSpace(data), nil
|
||||
}
|
||||
|
||||
56
plugin/aiimage/config.go
Normal file
56
plugin/aiimage/config.go
Normal file
@@ -0,0 +1,56 @@
|
||||
// Package aiimage 提供AI画图功能配置
|
||||
package aiimage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
)
|
||||
|
||||
// storage 管理画图配置存储
|
||||
type storage struct {
|
||||
sync.RWMutex
|
||||
db sql.Sqlite
|
||||
}
|
||||
|
||||
// imageConfig 存储AI画图配置信息
|
||||
type imageConfig struct {
|
||||
ID int64 `db:"id"` // 主键ID
|
||||
APIKey string `db:"apiKey"` // API密钥
|
||||
APIURL string `db:"apiUrl"` // API地址
|
||||
ModelName string `db:"modelName"` // 画图模型名称
|
||||
}
|
||||
|
||||
// getConfig 获取当前配置
|
||||
func (sdb *storage) getConfig() imageConfig {
|
||||
sdb.RLock()
|
||||
defer sdb.RUnlock()
|
||||
cfg := imageConfig{}
|
||||
_ = sdb.db.Find("config", &cfg, "WHERE id = 1")
|
||||
return cfg
|
||||
}
|
||||
|
||||
// setConfig 设置AI画图配置
|
||||
func (sdb *storage) setConfig(apiKey, apiURL, modelName string) error {
|
||||
sdb.Lock()
|
||||
defer sdb.Unlock()
|
||||
return sdb.db.Insert("config", &imageConfig{
|
||||
ID: 1,
|
||||
APIKey: apiKey,
|
||||
APIURL: apiURL,
|
||||
ModelName: modelName,
|
||||
})
|
||||
}
|
||||
|
||||
// PrintConfig 返回格式化后的配置信息
|
||||
func (sdb *storage) PrintConfig() string {
|
||||
cfg := sdb.getConfig()
|
||||
var builder strings.Builder
|
||||
builder.WriteString("当前AI画图配置:\n")
|
||||
builder.WriteString(fmt.Sprintf("• 密钥: %s\n", cfg.APIKey))
|
||||
builder.WriteString(fmt.Sprintf("• 接口地址: %s\n", cfg.APIURL))
|
||||
builder.WriteString(fmt.Sprintf("• 模型名: %s\n", cfg.ModelName))
|
||||
return builder.String()
|
||||
}
|
||||
171
plugin/aiimage/main.go
Normal file
171
plugin/aiimage/main.go
Normal file
@@ -0,0 +1,171 @@
|
||||
// Package aiimage AI画图
|
||||
package aiimage
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
fcext "github.com/FloatTech/floatbox/ctxext"
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
"github.com/tidwall/gjson"
|
||||
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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var sdb = &storage{}
|
||||
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("aiimage"),
|
||||
Brief: "AI画图",
|
||||
Help: "- 设置AI画图密钥xxx\n" +
|
||||
"- 设置AI画图接口地址https://api.siliconflow.cn/v1/images/generations\n" +
|
||||
"- 设置AI画图模型名Kwai-Kolors/Kolors\n" +
|
||||
"- 查看AI画图配置\n" +
|
||||
"- AI画图 [描述]",
|
||||
PrivateDataFolder: "aiimage",
|
||||
})
|
||||
|
||||
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
sdb.db = sql.New(en.DataFolder() + "aiimage.db")
|
||||
err := sdb.db.Open(time.Hour)
|
||||
if err == nil {
|
||||
// 创建配置表
|
||||
err = sdb.db.Create("config", &imageConfig{})
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("[ERROR]:", err))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
ctx.SendChain(message.Text("[ERROR]:", err))
|
||||
return false
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI画图密钥", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
apiKey := strings.TrimSpace(ctx.State["args"].(string))
|
||||
cfg := sdb.getConfig()
|
||||
err := sdb.setConfig(apiKey, cfg.APIURL, cfg.ModelName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: 设置API密钥失败: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置API密钥"))
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI画图接口地址", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
apiURL := strings.TrimSpace(ctx.State["args"].(string))
|
||||
cfg := sdb.getConfig()
|
||||
err := sdb.setConfig(cfg.APIKey, apiURL, cfg.ModelName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: 设置API地址失败: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置API地址"))
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI画图模型名", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
modelName := strings.TrimSpace(ctx.State["args"].(string))
|
||||
cfg := sdb.getConfig()
|
||||
err := sdb.setConfig(cfg.APIKey, cfg.APIURL, modelName)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: 设置模型失败: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("成功设置模型: ", modelName))
|
||||
})
|
||||
|
||||
en.OnFullMatch("查看AI画图配置", getdb, zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(sdb.PrintConfig()))
|
||||
})
|
||||
|
||||
en.OnPrefix("AI画图", getdb).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text("少女思考中..."))
|
||||
prompt := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if prompt == "" {
|
||||
ctx.SendChain(message.Text("请输入图片描述"))
|
||||
return
|
||||
}
|
||||
|
||||
cfg := sdb.getConfig()
|
||||
if cfg.APIKey == "" || cfg.APIURL == "" || cfg.ModelName == "" {
|
||||
ctx.SendChain(message.Text("请先配置API密钥、地址和模型"))
|
||||
return
|
||||
}
|
||||
|
||||
// 准备请求数据
|
||||
reqBytes, _ := json.Marshal(map[string]interface{}{
|
||||
"model": cfg.ModelName,
|
||||
"prompt": prompt,
|
||||
"image_size": "1024x1024",
|
||||
"batch_size": 4,
|
||||
"num_inference_steps": 20,
|
||||
"guidance_scale": 7.5,
|
||||
})
|
||||
|
||||
// 发送API请求
|
||||
data, err := web.RequestDataWithHeaders(
|
||||
web.NewDefaultClient(),
|
||||
cfg.APIURL,
|
||||
"POST",
|
||||
func(req *http.Request) error {
|
||||
req.Header.Set("Authorization", "Bearer "+cfg.APIKey)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
return nil
|
||||
},
|
||||
bytes.NewReader(reqBytes),
|
||||
)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("API请求失败: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 解析API响应
|
||||
jsonData := gjson.ParseBytes(data)
|
||||
images := jsonData.Get("images")
|
||||
if !images.Exists() {
|
||||
images = jsonData.Get("data")
|
||||
if !images.Exists() {
|
||||
ctx.SendChain(message.Text("未获取到图片URL"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 发送生成的图片和相关信息
|
||||
inferenceTime := jsonData.Get("timings.inference").Float()
|
||||
seed := jsonData.Get("seed").Int()
|
||||
msg := make(message.Message, 0, 1)
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text("图片生成成功!\n",
|
||||
"提示词: ", prompt, "\n",
|
||||
"模型: ", cfg.ModelName, "\n",
|
||||
"推理时间: ", inferenceTime, "秒\n",
|
||||
"种子: ", seed)))
|
||||
|
||||
// 添加所有图片
|
||||
images.ForEach(func(_, value gjson.Result) bool {
|
||||
url := value.Get("url").String()
|
||||
if url != "" {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Image(url)))
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
134
plugin/airecord/record.go
Normal file
134
plugin/airecord/record.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package airecord 群应用:AI声聊
|
||||
package airecord
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/airecord"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
)
|
||||
|
||||
func init() {
|
||||
en := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Extra: control.ExtraFromString("airecord"),
|
||||
Brief: "群应用:AI声聊",
|
||||
Help: "- 设置AI语音群号1048452984(tips:机器人任意所在群聊即可)\n" +
|
||||
"- 设置AI语音模型\n" +
|
||||
"- 查看AI语音配置\n" +
|
||||
"- 发送AI语音xxx",
|
||||
PrivateDataFolder: "airecord",
|
||||
})
|
||||
|
||||
en.OnPrefix("设置AI语音群号", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
u := strings.TrimSpace(ctx.State["args"].(string))
|
||||
num, err := strconv.ParseInt(u, 10, 64)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: parse gid err: ", err))
|
||||
return
|
||||
}
|
||||
err = airecord.SetCustomGID(num)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set gid err: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("设置AI语音群号为", num))
|
||||
})
|
||||
en.OnFullMatch("设置AI语音模型", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
next := zero.NewFutureEvent("message", 999, false, ctx.CheckSession())
|
||||
recv, cancel := next.Repeat()
|
||||
defer cancel()
|
||||
jsonData := ctx.GetAICharacters(0, 1)
|
||||
|
||||
// 转换为字符串数组
|
||||
var names []string
|
||||
// 初始化两个映射表
|
||||
nameToID := make(map[string]string)
|
||||
nameToURL := make(map[string]string)
|
||||
characters := jsonData.Get("#.characters")
|
||||
|
||||
// 遍历每个角色对象
|
||||
characters.ForEach(func(_, group gjson.Result) bool {
|
||||
group.ForEach(func(_, character gjson.Result) bool {
|
||||
// 提取当前角色的三个字段
|
||||
name := character.Get("character_name").String()
|
||||
names = append(names, name)
|
||||
// 存入映射表(重复名称会覆盖,保留最后出现的条目)
|
||||
nameToID[name] = character.Get("character_id").String()
|
||||
nameToURL[name] = character.Get("preview_url").String()
|
||||
return true // 继续遍历
|
||||
})
|
||||
return true // 继续遍历
|
||||
})
|
||||
var builder strings.Builder
|
||||
// 写入开头文本
|
||||
builder.WriteString("请选择语音模型序号:\n")
|
||||
|
||||
// 遍历names数组,拼接序号和名称
|
||||
for i, v := range names {
|
||||
// 将数字转换为字符串(不依赖fmt)
|
||||
numStr := strconv.Itoa(i)
|
||||
// 拼接格式:"序号. 名称\n"
|
||||
builder.WriteString(numStr)
|
||||
builder.WriteString(". ")
|
||||
builder.WriteString(v)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
// 获取最终字符串
|
||||
ctx.SendChain(message.Text(builder.String()))
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Second * 120):
|
||||
ctx.SendChain(message.Text("设置AI语音模型指令过期"))
|
||||
return
|
||||
case ct := <-recv:
|
||||
msg := ct.Event.Message.ExtractPlainText()
|
||||
num, err := strconv.Atoi(msg)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("请输入数字!"))
|
||||
continue
|
||||
}
|
||||
if num < 0 || num >= len(names) {
|
||||
ctx.SendChain(message.Text("序号非法!"))
|
||||
continue
|
||||
}
|
||||
err = airecord.SetRecordModel(names[num], nameToID[names[num]])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: set model err: ", err))
|
||||
continue
|
||||
}
|
||||
ctx.SendChain(message.Text("已选择语音模型: ", names[num]))
|
||||
ctx.SendChain(message.Record(nameToURL[names[num]]))
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
en.OnFullMatch("查看AI语音配置", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SendChain(message.Text(airecord.PrintRecordConfig()))
|
||||
})
|
||||
en.OnPrefix("发送AI语音", zero.UserOrGrpAdmin).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
u := strings.TrimSpace(ctx.State["args"].(string))
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, u)
|
||||
if record == "" {
|
||||
id := ctx.SendGroupAIRecord(recCfg.ModelID, ctx.Event.GroupID, u)
|
||||
if id == "" {
|
||||
ctx.SendChain(message.Text("ERROR: get record err: empty record"))
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.SendChain(message.Record(record))
|
||||
})
|
||||
}
|
||||
@@ -17,7 +17,12 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
const bandur time.Duration = time.Minute * 10
|
||||
const (
|
||||
bandur time.Duration = time.Minute * 2
|
||||
add = "添加违禁词"
|
||||
del = "删除违禁词"
|
||||
list = "查看违禁词"
|
||||
)
|
||||
|
||||
var (
|
||||
managers *ctrl.Manager[*zero.Ctx] // managers lazy load
|
||||
@@ -41,7 +46,7 @@ func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "违禁词检测",
|
||||
Help: "- /[添加|删除|查看]违禁词",
|
||||
Help: "- [添加|删除|查看]违禁词",
|
||||
PrivateDataFolder: "anti_abuse",
|
||||
})
|
||||
|
||||
@@ -56,10 +61,14 @@ func init() {
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnMessage(onceRule, zero.OnlyGroup, func(ctx *zero.Ctx) bool {
|
||||
if !ctx.Event.IsToMe {
|
||||
return true
|
||||
notAntiabuse := func(ctx *zero.Ctx) bool {
|
||||
if zero.PrefixRule(add)(ctx) || zero.PrefixRule(del)(ctx) || zero.PrefixRule(list)(ctx) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
engine.OnMessage(onceRule, notAntiabuse, zero.OnlyGroup, func(ctx *zero.Ctx) bool {
|
||||
uid := ctx.Event.UserID
|
||||
gid := ctx.Event.GroupID
|
||||
msg := strings.ReplaceAll(ctx.MessageString(), "\n", "")
|
||||
@@ -70,7 +79,8 @@ func init() {
|
||||
if err := ctx.State["manager"].(*ctrl.Control[*zero.Ctx]).Manager.DoBlock(uid); err == nil {
|
||||
t := time.Now().Unix()
|
||||
cache.Set(uid, struct{}{})
|
||||
ctx.SetThisGroupBan(uid, int64(bandur.Minutes()))
|
||||
ctx.SetThisGroupBan(uid, int64(bandur.Seconds()))
|
||||
ctx.DeleteMessage(ctx.Event.MessageID)
|
||||
ctx.SendChain(message.Text("检测到违禁词, 已封禁/屏蔽", bandur))
|
||||
db.Lock()
|
||||
defer db.Unlock()
|
||||
@@ -92,9 +102,9 @@ func init() {
|
||||
return true
|
||||
})
|
||||
|
||||
engine.OnCommand("添加违禁词", zero.OnlyGroup, zero.AdminPermission, onceRule).Handle(
|
||||
engine.OnPrefix(add, zero.OnlyGroup, zero.AdminPermission, onceRule).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
args := ctx.State["args"].(string)
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if err := db.insertWord(ctx.Event.GroupID, args); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else {
|
||||
@@ -102,9 +112,9 @@ func init() {
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnCommand("删除违禁词", zero.OnlyGroup, zero.AdminPermission, onceRule).Handle(
|
||||
engine.OnPrefix(del, zero.OnlyGroup, zero.AdminPermission, onceRule).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
args := ctx.State["args"].(string)
|
||||
args := strings.TrimSpace(ctx.State["args"].(string))
|
||||
if err := db.deleteWord(ctx.Event.GroupID, args); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
} else {
|
||||
@@ -112,7 +122,7 @@ func init() {
|
||||
}
|
||||
})
|
||||
|
||||
engine.OnCommand("查看违禁词", zero.OnlyGroup, onceRule).Handle(
|
||||
engine.OnPrefix(list, zero.OnlyGroup, onceRule).SetBlock(true).Handle(
|
||||
func(ctx *zero.Ctx) {
|
||||
b, err := text.RenderToBase64(db.listWords(ctx.Event.GroupID), text.FontFile, 400, 20)
|
||||
if err != nil {
|
||||
|
||||
@@ -24,8 +24,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
enableHex = 0x10
|
||||
unableHex = 0x7fffffff_fffffffd
|
||||
enableVideoSummary = int64(0x10)
|
||||
disableVideoSummary = ^enableVideoSummary
|
||||
enableVideoDownload = int64(0x20)
|
||||
disableVideoDownload = ^enableVideoDownload
|
||||
bilibiliparseReferer = "https://www.bilibili.com"
|
||||
)
|
||||
|
||||
@@ -92,9 +94,9 @@ func init() {
|
||||
data := c.GetData(ctx.Event.GroupID)
|
||||
switch option {
|
||||
case "开启", "打开", "启用":
|
||||
data |= enableHex
|
||||
data |= enableVideoSummary
|
||||
case "关闭", "关掉", "禁用":
|
||||
data &= unableHex
|
||||
data &= disableVideoSummary
|
||||
default:
|
||||
return
|
||||
}
|
||||
@@ -105,6 +107,35 @@ func init() {
|
||||
}
|
||||
ctx.SendChain(message.Text("已", option, "视频总结"))
|
||||
})
|
||||
en.OnRegex(`^(开启|打开|启用|关闭|关掉|禁用)视频上传$`, zero.AdminPermission).SetBlock(true).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
if gid <= 0 {
|
||||
// 个人用户设为负数
|
||||
gid = -ctx.Event.UserID
|
||||
}
|
||||
option := ctx.State["regex_matched"].([]string)[1]
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if !ok {
|
||||
ctx.SendChain(message.Text("找不到服务!"))
|
||||
return
|
||||
}
|
||||
data := c.GetData(ctx.Event.GroupID)
|
||||
switch option {
|
||||
case "开启", "打开", "启用":
|
||||
data |= enableVideoDownload
|
||||
case "关闭", "关掉", "禁用":
|
||||
data &= disableVideoDownload
|
||||
default:
|
||||
return
|
||||
}
|
||||
err := c.SetData(gid, data)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("出错啦: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("已", option, "视频上传"))
|
||||
})
|
||||
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)
|
||||
@@ -127,7 +158,7 @@ func handleVideo(ctx *zero.Ctx) {
|
||||
return
|
||||
}
|
||||
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
|
||||
if ok && c.GetData(ctx.Event.GroupID)&enableHex == enableHex {
|
||||
if ok && c.GetData(ctx.Event.GroupID)&enableVideoSummary == enableVideoSummary {
|
||||
summaryMsg, err := getVideoSummary(cfg, card)
|
||||
if err != nil {
|
||||
msg = append(msg, message.Text("ERROR: ", err))
|
||||
@@ -136,12 +167,14 @@ func handleVideo(ctx *zero.Ctx) {
|
||||
}
|
||||
}
|
||||
ctx.SendChain(msg...)
|
||||
downLoadMsg, err := getVideoDownload(cfg, card, cachePath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
if ok && c.GetData(ctx.Event.GroupID)&enableVideoDownload == enableVideoDownload {
|
||||
downLoadMsg, err := getVideoDownload(cfg, card, cachePath)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(downLoadMsg...)
|
||||
}
|
||||
ctx.SendChain(downLoadMsg...)
|
||||
}
|
||||
|
||||
func handleDynamic(ctx *zero.Ctx) {
|
||||
@@ -163,7 +196,12 @@ func handleArticle(ctx *zero.Ctx) {
|
||||
}
|
||||
|
||||
func handleLive(ctx *zero.Ctx) {
|
||||
card, err := bz.GetLiveRoomInfo(ctx.State["regex_matched"].([]string)[1])
|
||||
cookie, err := cfg.Load()
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
card, err := bz.GetLiveRoomInfo(ctx.State["regex_matched"].([]string)[1], cookie)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestVideoInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLiveRoomInfo(t *testing.T) {
|
||||
card, err := bz.GetLiveRoomInfo("83171")
|
||||
card, err := bz.GetLiveRoomInfo("83171", "b_ut=7;buvid3=0;i-wanna-go-back=-1;innersign=0;")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -43,8 +43,15 @@ func init() {
|
||||
})
|
||||
|
||||
engine.OnPrefix(`查询水群`, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
param := ctx.State["args"].(string)
|
||||
var uid int64
|
||||
if len(ctx.Event.Message) > 1 && ctx.Event.Message[1].Type == "at" {
|
||||
uid, _ = strconv.ParseInt(ctx.Event.Message[1].Data["qq"], 10, 64)
|
||||
} else if param == "" {
|
||||
uid = ctx.Event.UserID
|
||||
}
|
||||
name := ctx.NickName()
|
||||
todayTime, todayMessage, totalTime, totalMessage := ctdb.getChatTime(ctx.Event.GroupID, ctx.Event.UserID)
|
||||
todayTime, todayMessage, totalTime, totalMessage := ctdb.getChatTime(ctx.Event.GroupID, uid)
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(fmt.Sprintf("%s今天水了%d分%d秒,发了%d条消息;总计水了%d分%d秒,发了%d条消息。", name, todayTime/60, todayTime%60, todayMessage, totalTime/60, totalTime%60, totalMessage)))
|
||||
})
|
||||
engine.OnFullMatch("查看水群排名", zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).
|
||||
|
||||
95
plugin/crypter/fumo.go
Normal file
95
plugin/crypter/fumo.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Package crypter Fumo语
|
||||
package crypter
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Base64字符表
|
||||
const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
// Fumo语字符表 - 使用各种fumo变体来表示base64字符
|
||||
var fumoChars = []string{
|
||||
"fumo-", "Fumo-", "fUmo-", "fuMo-", "fumO-", "FUmo-", "FuMo-", "FumO-",
|
||||
"fUMo-", "fUmO-", "fuMO-", "FUMo-", "FUmO-", "fUMO-", "FUMO-", "fumo.",
|
||||
"Fumo.", "fUmo.", "fuMo.", "fumO.", "FUmo.", "FuMo.", "FumO.", "fUMo.",
|
||||
"fUmO.", "fuMO.", "FUMo.", "FUmO.", "fUMO.", "FUMO.", "fumo,", "Fumo,",
|
||||
"fUmo,", "fuMo,", "fumO,", "FUmo,", "FuMo,", "FumO,", "fUMo,", "fUmO,",
|
||||
"fuMO,", "FUMo,", "FuMO,", "fUMO,", "FUMO,", "fumo+", "Fumo+", "fUmo+",
|
||||
"fuMo+", "fumO+", "FUmo+", "FuMo+", "FumO+", "fUMo+", "fUmO+", "fuMO+",
|
||||
"FUMo+", "FUmO+", "fUMO+", "FUMO+", "fumo|", "Fumo|", "fUmo|", "fuMo|",
|
||||
"fumO|", "FUmo|", "FuMo|", "FumO|", "fUMo|", "fUmO|", "fuMO|", "fumo/",
|
||||
"Fumo/", "fUmo/",
|
||||
}
|
||||
|
||||
// Base64 2 Fumo
|
||||
// 创建编码映射表
|
||||
var encodeMap = make(map[byte]string)
|
||||
|
||||
// 创建解码映射表
|
||||
var decodeMap = make(map[string]byte)
|
||||
|
||||
func init() {
|
||||
for i := 0; i < 64 && i < len(fumoChars); i++ {
|
||||
base64Char := base64Chars[i]
|
||||
fumoChar := fumoChars[i]
|
||||
|
||||
encodeMap[base64Char] = fumoChar
|
||||
decodeMap[fumoChar] = base64Char
|
||||
}
|
||||
}
|
||||
|
||||
// 加密
|
||||
func encryptFumo(text string) string {
|
||||
if text == "" {
|
||||
return "请输入要加密的文本"
|
||||
}
|
||||
textBytes := []byte(text)
|
||||
base64String := base64.StdEncoding.EncodeToString(textBytes)
|
||||
base64Body := strings.TrimRight(base64String, "=")
|
||||
paddingCount := len(base64String) - len(base64Body)
|
||||
var fumoBody strings.Builder
|
||||
for _, char := range base64Body {
|
||||
if fumoChar, exists := encodeMap[byte(char)]; exists {
|
||||
fumoBody.WriteString(fumoChar)
|
||||
} else {
|
||||
return fmt.Sprintf("Fumo加密失败: 未知字符 %c", char)
|
||||
}
|
||||
}
|
||||
result := fumoBody.String() + strings.Repeat("=", paddingCount)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 解密
|
||||
func decryptFumo(fumoText string) string {
|
||||
if fumoText == "" {
|
||||
return "请输入要解密的Fumo语密文"
|
||||
}
|
||||
fumoBody := strings.TrimRight(fumoText, "=")
|
||||
paddingCount := len(fumoText) - len(fumoBody)
|
||||
fumoPattern := regexp.MustCompile(`(\w+[-.,+|/])`)
|
||||
fumoWords := fumoPattern.FindAllString(fumoBody, -1)
|
||||
reconstructed := strings.Join(fumoWords, "")
|
||||
if reconstructed != fumoBody {
|
||||
return "Fumo解密失败: 包含无效的Fumo字符或格式错误"
|
||||
}
|
||||
var base64Body strings.Builder
|
||||
for _, fumoWord := range fumoWords {
|
||||
if base64Char, exists := decodeMap[fumoWord]; exists {
|
||||
base64Body.WriteByte(base64Char)
|
||||
} else {
|
||||
return fmt.Sprintf("Fumo解密失败: 包含无效的Fumo字符 %s", fumoWord)
|
||||
}
|
||||
}
|
||||
base64String := base64Body.String() + strings.Repeat("=", paddingCount)
|
||||
decodedBytes, err := base64.StdEncoding.DecodeString(base64String)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Fumo解密失败: Base64解码错误 %v", err)
|
||||
}
|
||||
originalText := string(decodedBytes)
|
||||
return originalText
|
||||
}
|
||||
42
plugin/crypter/handlers.go
Normal file
42
plugin/crypter/handlers.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Package crypter 处理函数
|
||||
package crypter
|
||||
|
||||
import (
|
||||
"github.com/FloatTech/AnimeAPI/airecord"
|
||||
"github.com/sirupsen/logrus"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
// hou
|
||||
func houEncryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := encodeHou(text)
|
||||
logrus.Infoln("[crypter] 回复内容:", result)
|
||||
recCfg := airecord.GetConfig()
|
||||
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result)
|
||||
if record != "" {
|
||||
ctx.SendChain(message.Record(record))
|
||||
} else {
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
}
|
||||
|
||||
func houDecryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := decodeHou(text)
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
|
||||
// fumo
|
||||
func fumoEncryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := encryptFumo(text)
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
|
||||
func fumoDecryptHandler(ctx *zero.Ctx) {
|
||||
text := ctx.State["regex_matched"].([]string)[1]
|
||||
result := decryptFumo(text)
|
||||
ctx.SendChain(message.Text(result))
|
||||
}
|
||||
88
plugin/crypter/hou.go
Normal file
88
plugin/crypter/hou.go
Normal file
@@ -0,0 +1,88 @@
|
||||
// Package crypter 齁语加解密
|
||||
package crypter
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 齁语密码表
|
||||
var houCodebook = []string{
|
||||
"齁", "哦", "噢", "喔", "咕", "咿", "嗯", "啊",
|
||||
"~", "哈", "!", "唔", "哼", "❤", "呃", "呼",
|
||||
}
|
||||
|
||||
// 索引: 0 1 2 3 4 5 6 7
|
||||
// 8 9 10 11 12 13 14 15
|
||||
|
||||
// 创建映射表
|
||||
var houCodebookMap = make(map[string]int)
|
||||
|
||||
// 初始化映射表
|
||||
func init() {
|
||||
for idx, ch := range houCodebook {
|
||||
houCodebookMap[ch] = idx
|
||||
}
|
||||
}
|
||||
|
||||
func encodeHou(text string) string {
|
||||
if text == "" {
|
||||
return "请输入要加密的文本"
|
||||
}
|
||||
var encoded strings.Builder
|
||||
textBytes := []byte(text)
|
||||
for _, b := range textBytes {
|
||||
high := (b >> 4) & 0x0F
|
||||
low := b & 0x0F
|
||||
encoded.WriteString(houCodebook[high])
|
||||
encoded.WriteString(houCodebook[low])
|
||||
}
|
||||
|
||||
return encoded.String()
|
||||
}
|
||||
|
||||
func decodeHou(code string) string {
|
||||
if code == "" {
|
||||
return "请输入要解密的齁语密文"
|
||||
}
|
||||
|
||||
// 过滤出有效的齁语字符
|
||||
var validChars []string
|
||||
for _, r := range code {
|
||||
charStr := string(r)
|
||||
if _, exists := houCodebookMap[charStr]; exists {
|
||||
validChars = append(validChars, charStr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validChars)%2 != 0 {
|
||||
return "齁语密文长度错误,无法解密"
|
||||
}
|
||||
|
||||
// 解密过程
|
||||
var byteList []byte
|
||||
for i := 0; i < len(validChars); i += 2 {
|
||||
highIdx, highExists := houCodebookMap[validChars[i]]
|
||||
lowIdx, lowExists := houCodebookMap[validChars[i+1]]
|
||||
|
||||
if !highExists || !lowExists {
|
||||
return "齁语密文包含无效字符"
|
||||
}
|
||||
|
||||
originalByte := byte((highIdx << 4) | lowIdx)
|
||||
byteList = append(byteList, originalByte)
|
||||
}
|
||||
|
||||
result := string(byteList)
|
||||
|
||||
if !isValidUTF8(result) {
|
||||
return "齁语解密失败,结果不是有效的文本"
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 检查字符串是否为有效的UTF-8编码
|
||||
func isValidUTF8(s string) bool {
|
||||
// Go的string类型默认就是UTF-8,如果转换没有出错说明是有效的
|
||||
return len(s) > 0 || s == ""
|
||||
}
|
||||
31
plugin/crypter/main.go
Normal file
31
plugin/crypter/main.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package crypter 奇怪语言加解密
|
||||
package crypter
|
||||
|
||||
import (
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
zero "github.com/wdvxdr1123/ZeroBot"
|
||||
)
|
||||
|
||||
func init() {
|
||||
engine := control.Register("crypter", &ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "奇怪语言加解密",
|
||||
Help: "多种语言加解密插件\n" +
|
||||
"- 齁语加解密:\n" +
|
||||
"- 齁语加密 [文本] 或 h加密 [文本]\n" +
|
||||
"- 齁语解密 [密文] 或 h解密 [密文]\n\n" +
|
||||
"- Fumo语加解密:\n" +
|
||||
"- fumo加密 [文本]\n" +
|
||||
"- fumo解密 [密文]\n\n",
|
||||
PublicDataFolder: "Crypter",
|
||||
})
|
||||
|
||||
// hou
|
||||
engine.OnRegex(`^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
|
||||
engine.OnRegex(`^(?:齁语解密|h解密)\s*(.+)$`).SetBlock(true).Handle(houDecryptHandler)
|
||||
|
||||
// Fumo
|
||||
engine.OnRegex(`^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
|
||||
engine.OnRegex(`^fumo解密\s*(.+)$`).SetBlock(true).Handle(fumoDecryptHandler)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ func dlchan(name string, s *string, wg *sync.WaitGroup, exit func(error)) {
|
||||
defer wg.Done()
|
||||
target := datapath + `materials/` + name
|
||||
if file.IsNotExist(target) {
|
||||
data, err := web.GetData(`https://gitea.seku.su/fumiama/ImageMaterials/raw/branch/main/` + name)
|
||||
data, err := web.GetData(`https://gitea.seku.su/fumiama/ImageMaterials/raw/branch/master/` + name)
|
||||
if err != nil {
|
||||
_ = os.Remove(target)
|
||||
exit(err)
|
||||
@@ -48,7 +48,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) {
|
||||
data, err := web.GetData(`https://gitea.seku.su/fumiama/ImageMaterials/raw/branch/main/` + name)
|
||||
data, err := web.GetData(`https://gitea.seku.su/fumiama/ImageMaterials/raw/branch/master/` + name)
|
||||
if err != nil {
|
||||
_ = os.Remove(target)
|
||||
return "", err
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
package kfccrazythursday
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/FloatTech/floatbox/web"
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
@@ -15,12 +13,6 @@ const (
|
||||
crazyURL = "https://api.pearktrue.cn/api/kfc/"
|
||||
)
|
||||
|
||||
type crazyResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
@@ -34,17 +26,7 @@ func init() {
|
||||
return
|
||||
}
|
||||
|
||||
var resp crazyResponse
|
||||
if err := json.Unmarshal(data, &resp); err != nil {
|
||||
ctx.SendChain(message.Text("JSON解析失败: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
if resp.Code != 200 {
|
||||
ctx.SendChain(message.Text("API返回错误: ", resp.Msg))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SendChain(message.Text(resp.Text))
|
||||
// 根据来源API修改返回方式到直接输出文本
|
||||
ctx.SendChain(message.Text(string(data)))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ const (
|
||||
"- 列出所有提醒\n" +
|
||||
"- 翻牌\n" +
|
||||
"- 赞我\n" +
|
||||
"- 群签到\n" +
|
||||
"- 对信息回复: 回应表情 [表情]\n" +
|
||||
"- 设置欢迎语XXX 可选添加 [{at}] [{nickname}] [{avatar}] [{uid}] [{gid}] [{groupname}]\n" +
|
||||
"- 测试欢迎语\n" +
|
||||
@@ -405,6 +406,12 @@ func init() { // 插件主体
|
||||
ctx.SendLike(ctx.Event.UserID, 10)
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("给你赞了10下哦,记得回我~"))
|
||||
})
|
||||
// 群签到
|
||||
engine.OnFullMatch("群签到", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
ctx.SetGroupSign(ctx.Event.GroupID)
|
||||
ctx.SendChain(message.Text("群签到成功,可在手机端输入框中的打卡查看"))
|
||||
})
|
||||
facere := regexp.MustCompile(`\[CQ:face,id=(\d+)\]`)
|
||||
// 给消息回应表情
|
||||
engine.OnRegex(`^\[CQ:reply,id=(-?\d+)\].*回应表情\s*(.+)\s*$`, zero.AdminPermission, zero.OnlyGroup).SetBlock(true).
|
||||
|
||||
@@ -21,6 +21,10 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
var (
|
||||
longZhuURL = "https://www.hhlqilongzhu.cn/api/joox/juhe_music.php?msg=%v"
|
||||
)
|
||||
|
||||
func init() {
|
||||
control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
@@ -29,7 +33,8 @@ func init() {
|
||||
"- 网易点歌[xxx]\n" +
|
||||
"- 酷我点歌[xxx]\n" +
|
||||
"- 酷狗点歌[xxx]\n" +
|
||||
"- 咪咕点歌[xxx]",
|
||||
"- 咪咕点歌[xxx]\n" +
|
||||
"- qq点歌[xxx]\n",
|
||||
}).OnRegex(`^(.{0,2})点歌\s?(.{1,25})$`).SetBlock(true).Limit(ctxext.LimitByUser).
|
||||
Handle(func(ctx *zero.Ctx) {
|
||||
// switch 平台
|
||||
@@ -42,12 +47,37 @@ func init() {
|
||||
ctx.SendChain(kugou(ctx.State["regex_matched"].([]string)[2]))
|
||||
case "网易":
|
||||
ctx.SendChain(cloud163(ctx.State["regex_matched"].([]string)[2]))
|
||||
default: // 默认 QQ音乐
|
||||
case "qq":
|
||||
ctx.SendChain(qqmusic(ctx.State["regex_matched"].([]string)[2]))
|
||||
default: // 默认聚合点歌
|
||||
ctx.SendChain(longzhu(ctx.State["regex_matched"].([]string)[2]))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// longzhu 聚合平台
|
||||
func longzhu(keyword string) message.Segment {
|
||||
data, _ := web.GetData(fmt.Sprintf(longZhuURL, url.QueryEscape(keyword)))
|
||||
// 假设 data 是包含整个 JSON 数组的字节切片
|
||||
results := gjson.ParseBytes(data).Array()
|
||||
for _, result := range results {
|
||||
if strings.Contains(strings.ToLower(result.Get("title").String()), strings.ToLower(keyword)) {
|
||||
if musicURL := result.Get("full_track").String(); musicURL != "" {
|
||||
return message.Record(musicURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results = gjson.GetBytes(data, "#.full_track").Array()
|
||||
if len(results) > 0 {
|
||||
if musicURL := results[0].String(); musicURL != "" {
|
||||
return message.Record(musicURL)
|
||||
}
|
||||
}
|
||||
|
||||
return message.Text("点歌失败, 找不到 ", keyword, " 的相关结果")
|
||||
}
|
||||
|
||||
// migu 返回咪咕音乐卡片
|
||||
func migu(keyword string) message.Segment {
|
||||
headers := http.Header{
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
package niuniu
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/FloatTech/AnimeAPI/niu"
|
||||
@@ -18,12 +20,6 @@ import (
|
||||
"github.com/wdvxdr1123/ZeroBot/message"
|
||||
)
|
||||
|
||||
type lastLength struct {
|
||||
TimeLimit time.Time
|
||||
Count int
|
||||
Length float64
|
||||
}
|
||||
|
||||
var (
|
||||
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
@@ -47,8 +43,8 @@ var (
|
||||
})
|
||||
dajiaoLimiter = rate.NewManager[string](time.Second*90, 1)
|
||||
jjLimiter = rate.NewManager[string](time.Second*150, 1)
|
||||
jjCount = syncx.Map[string, *lastLength]{}
|
||||
register = syncx.Map[string, *lastLength]{}
|
||||
jjCount = syncx.Map[string, *niu.PKRecord]{}
|
||||
register = syncx.Map[string, *niu.PKRecord]{}
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -65,7 +61,7 @@ func init() {
|
||||
messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text("牛牛拍卖行有以下牛牛")))
|
||||
for _, info := range auction {
|
||||
msg := fmt.Sprintf("商品序号: %d\n牛牛原所属: %d\n牛牛价格: %d%s\n牛牛大小: %.2fcm",
|
||||
info.ID+1, info.UserID, info.Money, wallet.GetWalletName(), info.Length)
|
||||
info.ID, info.UserID, info.Money, wallet.GetWalletName(), info.Length)
|
||||
messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text(msg)))
|
||||
}
|
||||
if id := ctx.Send(messages).ID(); id == 0 {
|
||||
@@ -81,7 +77,7 @@ func init() {
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
ctx.SendChain(message.At(uid), message.Text(" 超时,已自动取消"))
|
||||
ctx.SendChain(message.At(uid), message.Text(" 超时,已自动取消"))
|
||||
return
|
||||
case r := <-recv:
|
||||
answer = r.Event.Message.String()
|
||||
@@ -90,7 +86,6 @@ func init() {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
n--
|
||||
msg, err := niu.Auction(gid, uid, n)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
@@ -104,11 +99,21 @@ func init() {
|
||||
en.OnFullMatch("出售牛牛", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
gid := ctx.Event.GroupID
|
||||
uid := ctx.Event.UserID
|
||||
key := fmt.Sprintf("%d_%d", gid, uid)
|
||||
sell, err := niu.Sell(gid, uid)
|
||||
if err != nil {
|
||||
if errors.Is(err, niu.ErrCanceled) || errors.Is(err, niu.ErrNoNiuNiu) {
|
||||
ctx.SendChain(message.Text(err))
|
||||
jjCount.Delete(key)
|
||||
return
|
||||
} else if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 数据库操作成功之后,及时删除残留的缓存
|
||||
if _, ok := jjCount.Load(key); ok {
|
||||
jjCount.Delete(key)
|
||||
}
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(sell))
|
||||
})
|
||||
en.OnFullMatch("牛牛背包", zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
|
||||
@@ -135,29 +140,33 @@ func init() {
|
||||
cost int
|
||||
scope string
|
||||
description string
|
||||
count int
|
||||
}{
|
||||
1: {"伟哥", 300, "打胶", "可以让你打胶每次都增长", 5},
|
||||
2: {"媚药", 300, "打胶", "可以让你打胶每次都减少", 5},
|
||||
3: {"击剑神器", 500, "jj", "可以让你每次击剑都立于不败之地", 2},
|
||||
4: {"击剑神稽", 500, "jj", "可以让你每次击剑都失败", 2},
|
||||
1: {"伟哥", 100, "打胶", "可以让你打胶每次都增长"},
|
||||
2: {"媚药", 100, "打胶", "可以让你打胶每次都减少"},
|
||||
3: {"击剑神器", 300, "jj", "可以让你每次击剑都立于不败之地"},
|
||||
4: {"击剑神稽", 300, "jj", "可以让你每次击剑都失败"},
|
||||
}
|
||||
|
||||
var messages message.Message
|
||||
messages = append(messages, ctxext.FakeSenderForwardNode(ctx,
|
||||
message.Text("输入对应序号进行购买商品"),
|
||||
message.Text(
|
||||
"使用说明:\n"+
|
||||
"商品id-商品数量\n"+
|
||||
"如想购买10个伟哥\n"+
|
||||
"即:1-10")))
|
||||
messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text("牛牛商店当前售卖的物品如下")))
|
||||
for id := range propMap {
|
||||
for id := 1; id <= len(propMap); id++ {
|
||||
product := propMap[id]
|
||||
productInfo := fmt.Sprintf("商品%d\n商品名: %s\n商品价格: %dATRI币\n商品作用域: %s\n商品描述: %s\n使用次数:%d",
|
||||
id, product.name, product.cost, product.scope, product.description, product.count)
|
||||
productInfo := fmt.Sprintf("商品%d\n商品名: %s\n商品价格: %dATRI币\n商品作用域: %s\n商品描述: %s",
|
||||
id, product.name, product.cost, product.scope, product.description)
|
||||
messages = append(messages, ctxext.FakeSenderForwardNode(ctx, message.Text(productInfo)))
|
||||
}
|
||||
if id := ctx.Send(messages).ID(); id == 0 {
|
||||
ctx.Send(message.Text("发送商店失败"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SendChain(message.Text("输入对应序号进行购买商品"))
|
||||
recv, cancel := zero.NewFutureEvent("message", 999, false, zero.CheckUser(uid), zero.CheckGroup(gid), zero.RegexRule(`^(\d+)$`)).Repeat()
|
||||
recv, cancel := zero.NewFutureEvent("message", 999, false, zero.CheckUser(uid), zero.CheckGroup(gid), zero.RegexRule(`^(\d+)-(\d+)$`)).Repeat()
|
||||
defer cancel()
|
||||
timer := time.NewTimer(120 * time.Second)
|
||||
answer := ""
|
||||
@@ -165,17 +174,17 @@ func init() {
|
||||
for {
|
||||
select {
|
||||
case <-timer.C:
|
||||
ctx.SendChain(message.At(uid), message.Text(" 超时,已自动取消"))
|
||||
ctx.SendChain(message.At(uid), message.Text(" 超时,已自动取消"))
|
||||
return
|
||||
case r := <-recv:
|
||||
answer = r.Event.Message.String()
|
||||
n, err := strconv.Atoi(answer)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
|
||||
if err = niu.Store(gid, uid, n); err != nil {
|
||||
// 解析输入的商品ID和数量
|
||||
parts := strings.Split(answer, "-")
|
||||
productID, _ := strconv.Atoi(parts[0])
|
||||
quantity, _ := strconv.Atoi(parts[1])
|
||||
|
||||
if err := niu.Store(gid, uid, productID, quantity); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
return
|
||||
}
|
||||
@@ -196,16 +205,16 @@ func init() {
|
||||
}
|
||||
|
||||
if time.Since(last.TimeLimit) > time.Hour {
|
||||
ctx.SendChain(message.Text("时间已经过期了,牛牛已被收回!"))
|
||||
ctx.SendChain(message.Text("时间已经过期了,牛牛已被收回!"))
|
||||
jjCount.Delete(fmt.Sprintf("%d_%d", gid, uid))
|
||||
return
|
||||
}
|
||||
|
||||
if last.Count < 4 {
|
||||
ctx.SendChain(message.Text("你还没有被厥够4次呢,不能赎牛牛"))
|
||||
ctx.SendChain(message.Text("你还没有被厥够4次呢,不能赎牛牛"))
|
||||
return
|
||||
}
|
||||
ctx.SendChain(message.Text("再次确认一下哦,这次赎牛牛,牛牛长度将会变成", last.Length, "cm\n还需要嘛【是|否】"))
|
||||
ctx.SendChain(message.Text("再次确认一下哦,这次赎牛牛,牛牛长度将会变成", last.Length, "cm\n还需要嘛【是|否】"))
|
||||
recv, cancel := zero.NewFutureEvent("message", 999, false, zero.CheckUser(uid), zero.CheckGroup(gid), zero.RegexRule(`^(是|否)$`)).Repeat()
|
||||
defer cancel()
|
||||
timer := time.NewTimer(2 * time.Minute)
|
||||
@@ -222,11 +231,11 @@ func init() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := niu.Redeem(gid, uid, last.Length); err == nil {
|
||||
if err := niu.Redeem(gid, uid, *last); err != nil {
|
||||
ctx.SendChain(message.Text("ERROR:", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 成功赎回,删除残留的缓存。
|
||||
jjCount.Delete(fmt.Sprintf("%d_%d", gid, uid))
|
||||
|
||||
ctx.SendChain(message.At(uid), message.Text(fmt.Sprintf("恭喜你!成功赎回牛牛,当前长度为:%.2fcm", last.Length)))
|
||||
@@ -332,7 +341,7 @@ func init() {
|
||||
}
|
||||
uid := ctx.Event.UserID
|
||||
gid := ctx.Event.GroupID
|
||||
msg, length, err := niu.JJ(gid, uid, adduser, patternParsed[0].Text()[1])
|
||||
msg, length, niuID, err := niu.JJ(gid, uid, adduser, patternParsed[0].Text()[1])
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
jjLimiter.Delete(fmt.Sprintf("%d_%d", ctx.Event.GroupID, ctx.Event.UserID))
|
||||
@@ -341,22 +350,27 @@ func init() {
|
||||
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(msg))
|
||||
j := fmt.Sprintf("%d_%d", gid, adduser)
|
||||
count, ok := jjCount.Load(j)
|
||||
var c lastLength
|
||||
// 按照最后一次被jj时的时间计算,超过60分钟则重置
|
||||
var c niu.PKRecord
|
||||
// 按照最后一次被 jj 时的时间计算,超过60分钟则重置
|
||||
if !ok {
|
||||
c = lastLength{
|
||||
// 第一次被 jj
|
||||
c = niu.PKRecord{
|
||||
NiuID: niuID,
|
||||
TimeLimit: time.Now(),
|
||||
Count: 1,
|
||||
Length: length,
|
||||
}
|
||||
} else {
|
||||
c = lastLength{
|
||||
c = niu.PKRecord{
|
||||
NiuID: niuID,
|
||||
TimeLimit: time.Now(),
|
||||
Count: count.Count + 1,
|
||||
Length: count.Length,
|
||||
}
|
||||
// 超时了,重置
|
||||
if time.Since(c.TimeLimit) > time.Hour {
|
||||
c = lastLength{
|
||||
c = niu.PKRecord{
|
||||
NiuID: niuID,
|
||||
TimeLimit: time.Now(),
|
||||
Count: 1,
|
||||
Length: length,
|
||||
@@ -372,6 +386,9 @@ func init() {
|
||||
)))
|
||||
|
||||
if c.Count >= 4 {
|
||||
if c.Count == 6 {
|
||||
return
|
||||
}
|
||||
id := ctx.SendPrivateMessage(adduser,
|
||||
message.Text(fmt.Sprintf("你在%d群里已经被厥冒烟了,快去群里赎回你原本的牛牛!\n发送:`赎牛牛`即可!", gid)))
|
||||
if id == 0 {
|
||||
@@ -386,8 +403,8 @@ func init() {
|
||||
key := fmt.Sprintf("%d_%d", gid, uid)
|
||||
data, ok := register.Load(key)
|
||||
switch {
|
||||
case !ok || time.Since(data.TimeLimit) > time.Hour*12:
|
||||
data = &lastLength{
|
||||
case !ok || time.Since(data.TimeLimit) > time.Hour*24:
|
||||
data = &niu.PKRecord{
|
||||
TimeLimit: time.Now(),
|
||||
Count: 1,
|
||||
}
|
||||
@@ -396,6 +413,7 @@ func init() {
|
||||
ctx.SendChain(message.Text("你的钱不够你注销牛牛了,这次注销需要", data.Count*50, wallet.GetWalletName()))
|
||||
return
|
||||
}
|
||||
data.Count++
|
||||
}
|
||||
register.Store(key, data)
|
||||
msg, err := niu.Cancel(gid, uid)
|
||||
|
||||
@@ -2,6 +2,7 @@ package score
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
@@ -11,7 +12,10 @@ import (
|
||||
var sdb *scoredb
|
||||
|
||||
// scoredb 分数数据库
|
||||
type scoredb gorm.DB
|
||||
type scoredb struct {
|
||||
db *gorm.DB
|
||||
scoremu sync.Mutex
|
||||
}
|
||||
|
||||
// scoretable 分数结构体
|
||||
type scoretable struct {
|
||||
@@ -52,25 +56,31 @@ func initialize(dbpath string) *scoredb {
|
||||
panic(err)
|
||||
}
|
||||
gdb.AutoMigrate(&scoretable{}).AutoMigrate(&signintable{})
|
||||
return (*scoredb)(gdb)
|
||||
return &scoredb{
|
||||
db: gdb,
|
||||
}
|
||||
}
|
||||
|
||||
// Close ...
|
||||
func (sdb *scoredb) Close() error {
|
||||
db := (*gorm.DB)(sdb)
|
||||
db := sdb.db
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
// GetScoreByUID 取得分数
|
||||
func (sdb *scoredb) GetScoreByUID(uid int64) (s scoretable) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
sdb.scoremu.Lock()
|
||||
defer sdb.scoremu.Unlock()
|
||||
db := sdb.db
|
||||
db.Model(&scoretable{}).FirstOrCreate(&s, "uid = ? ", uid)
|
||||
return s
|
||||
}
|
||||
|
||||
// InsertOrUpdateScoreByUID 插入或更新分数
|
||||
func (sdb *scoredb) InsertOrUpdateScoreByUID(uid int64, score int) (err error) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
sdb.scoremu.Lock()
|
||||
defer sdb.scoremu.Unlock()
|
||||
db := sdb.db
|
||||
s := scoretable{
|
||||
UID: uid,
|
||||
Score: score,
|
||||
@@ -91,14 +101,18 @@ func (sdb *scoredb) InsertOrUpdateScoreByUID(uid int64, score int) (err error) {
|
||||
|
||||
// GetSignInByUID 取得签到次数
|
||||
func (sdb *scoredb) GetSignInByUID(uid int64) (si signintable) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
sdb.scoremu.Lock()
|
||||
defer sdb.scoremu.Unlock()
|
||||
db := sdb.db
|
||||
db.Model(&signintable{}).FirstOrCreate(&si, "uid = ? ", uid)
|
||||
return si
|
||||
}
|
||||
|
||||
// InsertOrUpdateSignInCountByUID 插入或更新签到次数
|
||||
func (sdb *scoredb) InsertOrUpdateSignInCountByUID(uid int64, count int) (err error) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
sdb.scoremu.Lock()
|
||||
defer sdb.scoremu.Unlock()
|
||||
db := sdb.db
|
||||
si := signintable{
|
||||
UID: uid,
|
||||
Count: count,
|
||||
@@ -118,7 +132,9 @@ func (sdb *scoredb) InsertOrUpdateSignInCountByUID(uid int64, count int) (err er
|
||||
}
|
||||
|
||||
func (sdb *scoredb) GetScoreRankByTopN(n int) (st []scoretable, err error) {
|
||||
db := (*gorm.DB)(sdb)
|
||||
sdb.scoremu.Lock()
|
||||
defer sdb.scoremu.Unlock()
|
||||
db := sdb.db
|
||||
err = db.Model(&scoretable{}).Order("score desc").Limit(n).Find(&st).Error
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-ego/gse"
|
||||
@@ -40,7 +39,7 @@ func init() {
|
||||
engine := control.AutoRegister(&ctrl.Options[*zero.Ctx]{
|
||||
DisableOnDefault: false,
|
||||
Brief: "聊天热词",
|
||||
Help: "- 热词 [群号] [消息数目]|热词 123456 1000",
|
||||
Help: "- 热词 [消息数目]|热词 1000",
|
||||
PublicDataFolder: "WordCount",
|
||||
})
|
||||
cachePath := engine.DataFolder() + "cache/"
|
||||
@@ -51,7 +50,7 @@ func init() {
|
||||
}
|
||||
_ = os.RemoveAll(cachePath)
|
||||
_ = os.MkdirAll(cachePath, 0755)
|
||||
engine.OnRegex(`^热词\s?(\d*)\s?(\d*)$`, zero.OnlyGroup, fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
engine.OnRegex(`^热词\s?(\d*)$`, zero.OnlyGroup, fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
|
||||
_, err := engine.GetLazyData("stopwords.txt", false)
|
||||
if err != nil {
|
||||
ctx.SendChain(message.Text("ERROR: ", err))
|
||||
@@ -85,17 +84,14 @@ func init() {
|
||||
}
|
||||
|
||||
ctx.SendChain(message.Text("少女祈祷中..."))
|
||||
gid, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if p > 10000 {
|
||||
p = 10000
|
||||
}
|
||||
if p == 0 {
|
||||
p = 1000
|
||||
}
|
||||
if gid == 0 {
|
||||
gid = ctx.Event.GroupID
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
group := ctx.GetGroupInfo(gid, false)
|
||||
if group.MemberCount == 0 {
|
||||
ctx.SendChain(message.Text(zero.BotConfig.NickName[0], "未加入", group.Name, "(", gid, "),无法获得热词呢"))
|
||||
@@ -108,44 +104,22 @@ func init() {
|
||||
return
|
||||
}
|
||||
messageMap := make(map[string]int, 256)
|
||||
msghists := make(chan *gjson.Result, 256)
|
||||
go func() {
|
||||
h := ctx.GetLatestGroupMessageHistory(gid)
|
||||
messageSeq := h.Get("messages.0.message_seq").Int()
|
||||
msghists <- &h
|
||||
for i := 1; i < int(p/20) && messageSeq != 0; i++ {
|
||||
h := ctx.GetGroupMessageHistory(gid, messageSeq)
|
||||
msghists <- &h
|
||||
messageSeq = h.Get("messages.0.message_seq").Int()
|
||||
}
|
||||
close(msghists)
|
||||
}()
|
||||
var wg sync.WaitGroup
|
||||
var mapmu sync.Mutex
|
||||
for h := range msghists {
|
||||
wg.Add(1)
|
||||
go func(h *gjson.Result) {
|
||||
for _, v := range h.Get("messages.#.message").Array() {
|
||||
tex := strings.TrimSpace(message.ParseMessageFromString(v.Str).ExtractPlainText())
|
||||
if tex == "" {
|
||||
continue
|
||||
}
|
||||
segments := seg.Segment(helper.StringToBytes(tex))
|
||||
words := gse.ToSlice(segments, true)
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
i := sort.SearchStrings(stopwords, word)
|
||||
if re.MatchString(word) && (i >= len(stopwords) || stopwords[i] != word) {
|
||||
mapmu.Lock()
|
||||
messageMap[word]++
|
||||
mapmu.Unlock()
|
||||
}
|
||||
h := ctx.GetGroupMessageHistory(gid, 0, p, false)
|
||||
h.Get("messages").ForEach(func(_, msgObj gjson.Result) bool {
|
||||
tex := strings.TrimSpace(message.ParseMessageFromString(msgObj.Get("raw_message").Str).ExtractPlainText())
|
||||
if tex != "" {
|
||||
segments := seg.Segment(helper.StringToBytes(tex))
|
||||
words := gse.ToSlice(segments, true)
|
||||
for _, word := range words {
|
||||
word = strings.TrimSpace(word)
|
||||
i := sort.SearchStrings(stopwords, word)
|
||||
if re.MatchString(word) && (i >= len(stopwords) || stopwords[i] != word) {
|
||||
messageMap[word]++
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(h)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
wc := rankByWordCount(messageMap)
|
||||
if len(wc) > 20 {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"0409": {
|
||||
"identity": {
|
||||
"name": "ZeroBot-Plugin",
|
||||
"version": "1.9.7.2217"
|
||||
"version": "1.9.9.2250"
|
||||
},
|
||||
"description": "",
|
||||
"minimum-os": "vista",
|
||||
@@ -36,23 +36,23 @@
|
||||
"#1": {
|
||||
"0000": {
|
||||
"fixed": {
|
||||
"file_version": "1.9.7.2217",
|
||||
"product_version": "v1.9.7",
|
||||
"timestamp": "2025-05-14T21:53:06+08:00"
|
||||
"file_version": "1.9.9.2250",
|
||||
"product_version": "v1.9.9",
|
||||
"timestamp": "2025-09-10T10:40:54+08:00"
|
||||
},
|
||||
"info": {
|
||||
"0409": {
|
||||
"Comments": "OneBot plugins based on ZeroBot",
|
||||
"CompanyName": "FloatTech",
|
||||
"FileDescription": "https://github.com/FloatTech/ZeroBot-Plugin",
|
||||
"FileVersion": "1.9.7.2217",
|
||||
"FileVersion": "1.9.9.2250",
|
||||
"InternalName": "",
|
||||
"LegalCopyright": "© 2020 - 2025 FloatTech. All Rights Reserved.",
|
||||
"LegalTrademarks": "",
|
||||
"OriginalFilename": "ZBP.EXE",
|
||||
"PrivateBuild": "",
|
||||
"ProductName": "ZeroBot-Plugin",
|
||||
"ProductVersion": "v1.9.7",
|
||||
"ProductVersion": "v1.9.9",
|
||||
"SpecialBuild": ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user