Compare commits

...

19 Commits

Author SHA1 Message Date
源文雨
fce75a9475 🔖 v1.5.2-beta2 2022-10-28 12:50:16 +08:00
源文雨
7616d5759f fix nsetu 循环触发 2022-10-28 11:40:58 +08:00
源文雨
ea2c81a9c7 Merge branch 'master' of https://github.com/FloatTech/ZeroBot-Plugin 2022-10-28 11:35:18 +08:00
源文雨
37cff9ff31 update reg 2022-10-28 11:35:11 +08:00
MoeMagicMango
b8d3d6af9b 修改漂流瓶的帮助 (#477) 2022-10-28 10:50:51 +08:00
MoeMagicMango
fdf90a72cc feat: 全局漂流瓶 (#474)
* feat: remove driftbottle and push bottle global plugin.

* Reverse name

* change by requests
2022-10-28 00:24:57 +08:00
DreamZero
ba0c05a774 change: 新的缓存池key (#476) 2022-10-28 00:20:19 +08:00
DreamZero
3250ec14ac fix: tarot (#475)
* fix tarot get img

* change: use sendtoslef check cache

但是好像过慢

* change: use imgpool

* fix

感觉改不好了

* fix: 修改变量赋值顺序
2022-10-28 00:00:43 +08:00
源文雨
ce2f390361 update md5 2022-10-27 16:57:16 +08:00
方柳煜
71434232fe 修复help显示问题 (#473)
* 修复help显示问题

* Update command.go

* Update wenxinAI.go

* Update README.md
2022-10-27 11:04:10 +08:00
GenesisAN
852629fa2e add:百度内容审核插件 (#468) 2022-10-25 18:43:58 +08:00
源文雨
f10676d16d 🔖 v1.5.2-beta1 2022-10-24 23:46:59 +08:00
方柳煜
d43ea7df1d 扩增文心AI的功能,修复猜歌的权限问题 (#471) 2022-10-24 23:36:28 +08:00
方柳煜
21712c6299 扩展aipaint指令 (#472) 2022-10-24 23:35:43 +08:00
himawari
2fc47a38fa 🎨 优化aipaint撤回,nihongo搜索 (#470) 2022-10-23 16:50:47 +08:00
源文雨
cbb4408668 fix tarot 2022-10-22 12:12:46 +08:00
源文雨
a0df41b859 fix tarot 2022-10-22 12:01:45 +08:00
DreamZero
494c1b33b4 fix: 因为gitcode反爬导致tarot插件图片获取失败 (#465)
* fix tarot get img

* change: use sendtoslef check cache

但是好像过慢

* change: use imgpool

* fix

感觉改不好了
2022-10-22 11:53:46 +08:00
DreamZero
2034d91912 fix: 以图搜图合并转发 (#467) 2022-10-20 00:17:00 +08:00
19 changed files with 1260 additions and 285 deletions

109
README.md
View File

@@ -26,7 +26,7 @@
| [Mrs4s/go-cqhttp](https://github.com/Mrs4s/go-cqhttp) | [MiraiGo](https://github.com/Mrs4s/MiraiGo) | Mrs4s |
| [yyuueexxiinngg/cqhttp-mirai](https://github.com/yyuueexxiinngg/cqhttp-mirai) | [Mirai](https://github.com/mamoe/mirai) | yyuueexxiinngg |
| [takayama-lily/onebot](https://github.com/takayama-lily/onebot) | [OICQ](https://github.com/takayama-lily/oicq) | takayama |
</div>
> 如果您不知道什么是 [OneBot](https://github.com/howmanybots/onebot) 或不希望运行多个程序,还可以直接前往 [gocqzbp](https://github.com/FloatTech/gocqzbp) 的 [Release](https://github.com/FloatTech/gocqzbp/releases) 页面下载单一可执行文件或前往 [Packages](https://github.com/FloatTech/gocqzbp/pkgs/container/gocqzbp) 页面使用`docker`,运行后按提示登录即可。
@@ -354,13 +354,15 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] [ ai绘图 | 生成色图 | 生成涩图 | ai画图 ] xxx
- [x] [ ai高级绘图 | 高级生成色图 | 高级生成涩图 | ai高级画图 ] xxx
- [x] [ 以图绘图 | 以图生图 | 以图画图 ] xxx [图片]|@xxx|[qq号]
- [ ] 设置ai绘图配置 [server] [token]
- [x] 设置ai绘图配置 [server] [token]
1: 设置ai绘图配置 http://91.216.169.75:5010 abc
例: 设置ai绘图配置 http://91.216.169.75:5010 abc
例2: 设置ai绘图配置 http://91.217.139.190:5010 abc
参考服务器 http://91.217.139.190:5010, http://91.216.169.75:5010, http://185.80.202.180:5010
通过 http://91.217.139.190:5010/token 获取token
@@ -403,6 +405,51 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 百度下[xxx]
</details>
<details>
<summary>百度内容审核</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit"`
- [x] 获取BDAkey
- [x] 配置BDAKey [API Key] [Secret Key]
- [x] 获取BDAkey
- [x] [开启|关闭]内容审核
- [x] [开启|关闭]撤回提示
- [x] [开启|关闭]详细提示
- [x] [开启|关闭]撤回禁言
- [x] [开启|关闭]禁言累加
- [x] [开启|关闭]文本检测
- [x] [开启|关闭]图像检测
- [x] 设置最大禁言时间[分钟,默认:60,最大43200]
- [x] 设置每次累加时间[分钟,默认:1]
- [x] 设置撤回禁言时间[分钟,默认:1]
- [x] 查看检测类型
- [x] 查看检测配置
- [x] 测试文本检测[文本内容]
- [x] 测试图像检测[图片]
- [x] 设置检测类型[类型编号]
- [x] 设置不检测类型[类型编号]
检测类型编号列表:[1:违禁违规|2:文本色情|3:敏感信息|4:恶意推广|5:低俗辱骂|6:恶意推广-联系方式|7:恶意推广-软文推广]
</details>
<details>
<summary>base64卦加解密</summary>
@@ -572,15 +619,9 @@ print("run[CQ:image,file="+j["img"]+"]")
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/drift_bottle"`
- [x] (在群xxx)丢漂流瓶(到频道xxx) [消息]
- [x] @Bot pick (随机捞一个漂流瓶)
- [x] (从频道xxx)捡漂流瓶
- [x] @BOT 创建频道 xxx
- [x] 跳入(频道)海中
- [x] 注:不显式限制时,私聊发送可在所有群抽到,群聊发送仅可在本群抽到,默认频道为 global
- [x] @Bot throw xxx (投递内容xxx,支持图片文字,投递内容需要大于10个字符或者带有图片)
</details>
<details>
@@ -962,6 +1003,8 @@ print("run[CQ:image,file="+j["img"]+"]")
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/nihongo"`
- [x] 日语语法 [xxx] (使用tag随机)
- [x] 搜索日语语法 [xxx]
</details>
<details>
@@ -1135,21 +1178,45 @@ print("run[CQ:image,file="+j["img"]+"]")
</details>
<details>
<summary>百度文心AI画图</summary>
<summary>百度文心AI</summary>
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/wenxinAI"`
- 基于百度文心的免费AI画图插件(因为是免费的,图片质量你懂的)
基于百度文心API的一些功能
- key申请链接https://wenxin.baidu.com/moduleApi/key
- [x] 为[自己/本群/QQ号/群+群号]设置AI画图key [API Key] [Secret Key]
-“为10086设置AI画图key 123 456”“为群10010设置AI画图key 789 101”
key申请链接https://wenxin.baidu.com/moduleApi/key
- [x] 为[自己/本群/QQ号/群+群号]设置文心key [API Key] [Secret Key]
- [x] 为[自己/本群/QQ号/群+群号]设置画图key [API Key] [Secret Key]
“为10086设置画图key 123 456”“为群10010设置画图key 789 101”
文心key和画图key的API key 可以是相同的只是文心key日限为200画图日限为50以此作区别。
- [x] 文心作文 (x字的)[作文题目]
- [x] 文心提案 (x字的)[文案标题]
- [x] 文心摘要 (x字的)[文章内容]
- [x] 文心小说 (x字的)[小说上文]
- [x] 文心对联 [上联]
- [x] 文心问答 [问题]
- [x] 文心补全 [带“_”的填空题]
- [x] 文心自定义 [prompt]
- [x] [bot名称]画几张[图片描述]的[图片类型][图片尺寸]
- 指令示例:
指令示例:
- 文心作文 我的椛椛机器人
- 文心作文 300字的我的椛椛机器人
- 椛椛帮我画几张金凤凰背景绚烂高饱和古风仙境高清4K古风的油画方图
@@ -1219,9 +1286,7 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] @Bot 任意文本(任意一句话回复)
- [x] 设置回复模式[青云客 | 小爱]
</details>
## 三种使用方法,推荐第一种
### 1. 使用稳定版/测试版 (推荐)

10
go.mod
View File

@@ -3,20 +3,21 @@ module github.com/FloatTech/ZeroBot-Plugin
go 1.19
require (
github.com/Baidu-AIP/golang-sdk v1.1.1
github.com/Coloured-glaze/gg v1.3.4
github.com/FloatTech/AnimeAPI v1.5.2-0.20221015060924-fe2f85a3cf45
github.com/FloatTech/floatbox v0.0.0-20221011153549-68005767c531
github.com/FloatTech/AnimeAPI v1.5.2-0.20221028033300-a9293e7eecb8
github.com/FloatTech/floatbox v0.0.0-20221028033104-a243fca6f2a3
github.com/FloatTech/sqlite v0.5.0
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b
github.com/FloatTech/zbpctrl v1.5.2-0.20221011153929-4834c6911511
github.com/FloatTech/zbputils v1.5.1-0.20221011154037-734498125e07
github.com/FloatTech/zbputils v1.5.1-0.20221028033223-1fdfaf978116
github.com/RomiChan/syncx v0.0.0-20220404072119-d7ea0ae15a4c
github.com/antchfx/htmlquery v1.2.5
github.com/corona10/goimagehash v1.1.0
github.com/fumiama/ahsai v0.1.0
github.com/fumiama/cron v1.3.0
github.com/fumiama/go-base16384 v1.6.1
github.com/fumiama/go-registry v0.1.6
github.com/fumiama/go-registry v0.2.1
github.com/fumiama/gotracemoe v0.0.3
github.com/fumiama/sqlite3 v1.14.6
github.com/fumiama/unibase2n v0.0.0-20221003115227-e7db987de949
@@ -43,6 +44,7 @@ require (
github.com/ericpauley/go-quantize v0.0.0-20200331213906-ae555eb2afa4 // indirect
github.com/faiface/beep v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/fumiama/go-simple-protobuf v0.1.0 // indirect
github.com/fumiama/gofastTEA v0.0.10 // indirect
github.com/gabriel-vasile/mimetype v1.0.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect

20
go.sum
View File

@@ -1,19 +1,21 @@
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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Coloured-glaze/gg v1.3.4 h1:l31zIF/HaVwkzjrj+A56RGQoSKyKuR1IWtIrqXGFStI=
github.com/Coloured-glaze/gg v1.3.4/go.mod h1:Ih5NLNNDHOy3RJbB0EPqGTreIzq/H02TGThIagh8HJg=
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/FloatTech/AnimeAPI v1.5.2-0.20221015060924-fe2f85a3cf45 h1:XbNlD0irJELgdR304TvqmFrxdH7hsxA/Ah9xLHFP3eQ=
github.com/FloatTech/AnimeAPI v1.5.2-0.20221015060924-fe2f85a3cf45/go.mod h1:RgcMDA1S7C81bq7HQjygMEEo+EXwAlsutvKMv7DafgY=
github.com/FloatTech/floatbox v0.0.0-20221011153549-68005767c531 h1:Z0yn6LFhEyC12hj+TBXc2P7/kWlCd/jlwv4JFndgpnw=
github.com/FloatTech/floatbox v0.0.0-20221011153549-68005767c531/go.mod h1:4tfIeB74L1RzhNp3nNjaTw8m3IEnc+q/k6k/MhL07ks=
github.com/FloatTech/AnimeAPI v1.5.2-0.20221028033300-a9293e7eecb8 h1:W+Gb+NGv6RJZI4BSJHHADzYC9YeDfftFGphP8aMUvbg=
github.com/FloatTech/AnimeAPI v1.5.2-0.20221028033300-a9293e7eecb8/go.mod h1:2RjLcJ67OxRebKTrxBKy4P/8wPdcVhci8a/HALVUAQQ=
github.com/FloatTech/floatbox v0.0.0-20221028033104-a243fca6f2a3 h1:yiq5kEr51FTfz7xxDY+xjbcdgDWyvZ6U93cf1xSsGs4=
github.com/FloatTech/floatbox v0.0.0-20221028033104-a243fca6f2a3/go.mod h1:GVbLqexg8XJGuZs2zBZHO7jswCAPkZ9M/IKBYD0I/L0=
github.com/FloatTech/sqlite v0.5.0 h1:U7J5Omc534PqmH6csfu+ypCo3DS8L91l5lTsxUu3b/U=
github.com/FloatTech/sqlite v0.5.0/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.5.2-0.20221011153929-4834c6911511 h1:i2+JjTRR7gW8n1KivMKj3NEsCEEllYp8jneeOyg1Lz0=
github.com/FloatTech/zbpctrl v1.5.2-0.20221011153929-4834c6911511/go.mod h1:TC5RkmSwKJvkDV+7vlTpcY+rnviB5gBq6JNOKnfZXHc=
github.com/FloatTech/zbputils v1.5.1-0.20221011154037-734498125e07 h1:nG1q2NJZPehyV0xk88WHtqgwsOohkEDMFu/8v9rN1/o=
github.com/FloatTech/zbputils v1.5.1-0.20221011154037-734498125e07/go.mod h1:UYuWZBInYdGZOXxEazrvaJE8nYGRB4xxoaacX6nKosk=
github.com/FloatTech/zbputils v1.5.1-0.20221028033223-1fdfaf978116 h1:ZHAMbLbSkirxV3/Z7FKaiF9TiMRCavCYsIbYDIYbSGc=
github.com/FloatTech/zbputils v1.5.1-0.20221028033223-1fdfaf978116/go.mod h1:nxTBx0YX8Ewq8oef2em4aJ5RH/5ZiG4PizK8v/csm7w=
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=
@@ -50,8 +52,10 @@ 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.6.1 h1:4yb4JgmBJDnQtq3XGXXdLrVwEnRpjhMUt4eAcsNeA30=
github.com/fumiama/go-base16384 v1.6.1/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/go-registry v0.2.1 h1:PCu4d1OIYkLmoSufyxov3QXRul4lXrAfXfK1pVS2wrQ=
github.com/fumiama/go-registry v0.2.1/go.mod h1:GP45kejHuDLFxcWdksrt75r5rHBqYwtfeUl3JzGWxfQ=
github.com/fumiama/go-simple-protobuf v0.1.0 h1:rLzJgNqB6LHNDVMl81yyNt6ZKziWtVfu+ioF0edlEVw=
github.com/fumiama/go-simple-protobuf v0.1.0/go.mod h1:5yYNapXq1tQMOZg9bOIVhQlZk9pQqpuFIO4DZLbsdy4=
github.com/fumiama/gofastTEA v0.0.10 h1:JJJ+brWD4kie+mmK2TkspDXKzqq0IjXm89aGYfoGhhQ=
github.com/fumiama/gofastTEA v0.0.10/go.mod h1:RIdbYZyB4MbH6ZBlPymRaXn3cD6SedlCu5W/HHfMPBk=
github.com/fumiama/gotracemoe v0.0.3 h1:iI5EbE9A3UUbfukG6+/soYPjp1S31eCNYf4tw7s6/Jc=

View File

@@ -9,7 +9,7 @@ import (
var (
// Banner ...
Banner = "* OneBot + ZeroBot + Golang\n" +
"* Version 1.5.1 - 2022-10-16 12:58:18 +0800 CST\n" +
"* Version 1.5.2-beta2 - 2022-10-28 12:49:51 +0800 CST\n" +
"* Copyright © 2020 - 2022 FloatTech. All Rights Reserved.\n" +
"* Project: https://github.com/FloatTech/ZeroBot-Plugin"
reg = registry.NewRegReader("reilia.fumiama.top:32664", "fumiama")

View File

@@ -65,6 +65,7 @@ import (
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/alipayvoice" // 支付宝到账语音
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/b14" // base16384加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baidu" // 百度一下
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baiduaudit" // 百度内容审核
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/base64gua" // base64卦加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/baseamasiro" // base天城文加解密
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/bilibili" // b站相关

View File

@@ -12,6 +12,7 @@ import (
"regexp"
"strconv"
"strings"
"time"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
@@ -52,11 +53,15 @@ func init() { // 插件主体
DisableOnDefault: false,
Help: "ai绘图\n" +
"- [ ai绘图 | 生成色图 | 生成涩图 | ai画图 ] xxx\n" +
"- [ ai高级绘图 | 高级生成色图 | 高级生成涩图 | ai高级画图 ] [prompt]\n" +
"- [ 以图绘图 | 以图生图 | 以图画图 ] xxx [图片]|@xxx|[qq号]\n" +
"- 设置ai绘图配置 [server] [token]\n" +
"例1: 设置ai绘图配置 http://91.216.169.75:5010 abc\n" +
"例2: 设置ai绘图配置 http://91.217.139.190:5010 abc\n" +
"通过 http://91.217.139.190:5010/token 获取token",
"例: 设置ai绘图配置 http://91.217.139.190:5010 abc\n" +
"参考服务器 http://91.217.139.190:5010, http://91.216.169.75:5010, http://185.80.202.180:5010\n" +
"通过 http://91.217.139.190:5010/token 获取token\n" +
"[prompt]参数如下\n" +
"tags:tag词条\nntags:ntag词条\nshape:[Portrait|Landscape|Square]\nscale:[6:20]\nseed:种子\n" +
"参数与参数内容用:连接,每个参数之间用回车或者&分割",
PrivateDataFolder: "aipaint",
})
datapath = file.BOTPATH + "/" + engine.DataFolder()
@@ -133,6 +138,56 @@ func init() { // 插件主体
}
sendAiImg(ctx, data)
})
engine.OnPrefixGroup([]string{`ai高级绘图`, `高级生成色图`, `高级生成涩图`, `ai高级画图`}).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
server, token, err := cfg.load()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
tags := make(map[string]string)
args := strings.Split(ctx.State["args"].(string), "\n")
if len(args) < 1 {
ctx.SendChain(message.Text("ERROR: 请输入正确的参数"))
return
}
for _, info := range args {
value := strings.Split(info, ":")
if len(value) > 1 {
if value[0] == "R18" && value[1] == "1" {
value[1] = "0"
ctx.SendChain(message.Text("不准涩涩!已将R18设置为0。"))
}
tags[value[0]] = strings.Join(value[1:], ":")
}
}
ctx.SendChain(message.Text("少女祈祷中..."))
apiurl := "/got_image?token=" + token
if _, ok := tags["tags"]; ok {
apiurl += "&tags=" + url.QueryEscape(strings.ReplaceAll(strings.TrimSpace(tags["tags"]), " ", "%20"))
}
if _, ok := tags["ntags"]; ok {
apiurl += "&ntags=" + url.QueryEscape(strings.ReplaceAll(strings.TrimSpace(tags["ntags"]), " ", "%20"))
}
if _, ok := tags["R18"]; ok {
apiurl += "&R18=" + url.QueryEscape(strings.TrimSpace(tags["R18"]))
}
if _, ok := tags["shape"]; ok {
apiurl += "&shape=" + url.QueryEscape(strings.TrimSpace(tags["shape"]))
}
if _, ok := tags["scale"]; ok {
apiurl += "&scale=" + url.QueryEscape(strings.TrimSpace(tags["scale"]))
}
if _, ok := tags["seed"]; ok {
apiurl += "&seed=" + url.QueryEscape(strings.TrimSpace(tags["seed"]))
}
data, err := web.GetData(server + apiurl)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
sendAiImg(ctx, data)
})
engine.OnRegex(`^设置ai绘图配置\s(.*[^\s$])\s(.+)$`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
regexMatched := ctx.State["regex_matched"].([]string)
@@ -157,11 +212,22 @@ func sendAiImg(ctx *zero.Ctx, data []byte) {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
r.Uc, err = url.QueryUnescape(r.Uc)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
}
encodeStr := base64.StdEncoding.EncodeToString(data)
m := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Image("base64://"+encodeStr))}
m = append(m, ctxext.FakeSenderForwardNode(ctx, message.Text(r.String())))
if id := ctx.Send(m).ID(); id == 0 {
if mid := ctx.Send(m); mid.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控或下载图片用时过长,请耐心等待"))
} else {
go func(i message.MessageID) {
time.Sleep(90 * time.Second)
ctx.DeleteMessage(i)
}(mid)
}
}

500
plugin/baiduaudit/audit.go Normal file
View File

@@ -0,0 +1,500 @@
// Package baiduaudit 百度内容审核
package baiduaudit
import (
"encoding/json"
"fmt"
"github.com/Baidu-AIP/golang-sdk/aip/censor"
"github.com/FloatTech/floatbox/binary"
"github.com/FloatTech/floatbox/file"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/img/text"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
"os"
"strconv"
)
// 服务网址:https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/index
// 返回参数说明https://cloud.baidu.com/doc/ANTIPORN/s/Nk3h6xbb2
type baiduRes struct {
LogID int `json:"log_id"` // 请求唯一id
Conclusion string `json:"conclusion"` // 审核结果,可取值:合规、不合规、疑似、审核失败
ConclusionType int `json:"conclusionType"` // 审核结果类型可取值1.合规2.不合规3.疑似4.审核失败
Data []auditData `json:"data"`
ErrorCode int `json:"error_code"` // 错误提示码,失败才返回,成功不返回
ErrorMsg string `json:"error_msg"` // 错误提示信息,失败才返回,成功不返回
}
type auditData struct {
Type int `json:"type"` // 审核主类型11百度官方违禁词库、12文本反作弊、13:自定义文本黑名单、14:自定义文本白名单
SubType int `json:"subType"` // 审核子类型0:含多种类型具体看官方链接1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广
Conclusion string `json:"conclusion"` // 审核结果,可取值:合规、不合规、疑似、审核失败
ConclusionType int `json:"conclusionType"` // 审核结果类型可取值1.合规2.不合规3.疑似4.审核失败
Msg string `json:"msg"` // 不合规项描述信息
Hits []hit `json:"hits"`
} // 不合规/疑似/命中白名单项详细信息。响应成功并且conclusion为疑似或不合规或命中白名单时才返回响应失败或conclusion为合规且未命中白名单时不返回。
type hit struct {
DatasetName string `json:"datasetName"` // 违规项目所属数据集名称
Words []string `json:"words"` // 送检文本命中词库的关键词备注建议参考新字段“wordHitPositions”包含信息更丰富关键词以及对应的位置及标签信息
Probability float64 `json:"probability,omitempty"` // 不合规项置信度
} // 送检文本违规原因的详细信息
type keyConfig struct {
Key1 string `json:"key1"` // 百度云服务内容审核key存储
Key2 string `json:"key2"` // 百度云服务内容审核key存储
Groups map[int64]group `json:"groups"` // 群配置存储
}
type group struct {
Enable EnableMark // 是否启用内容审核
TextAudit EnableMark // 文本检测
ImageAudit EnableMark // 图像检测
DMRemind EnableMark // 撤回提示
MoreRemind EnableMark // 详细违规提示
DMBAN EnableMark // 撤回后禁言
BANTimeAddEnable EnableMark // 禁言累加
BANTime int64 // 标准禁言时间,禁用累加,但开启禁言的的情况下采用该值
MaxBANTimeAddRange int64 // 最大禁言时间累加范围,最高禁言时间
BANTimeAddTime int64 // 禁言累加时间该值是开启禁累加功能后再次触发时根据被禁次数X该值计算出的禁言时间
WhiteListType [8]bool // 类型白名单,处于白名单类型的违规,不会被触发 0:含多种类型具体看官方链接1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广
AuditHistory map[int64]auditHistory // 被封禁用户列表
}
// EnableMark 启用:●,禁用:○
type EnableMark bool
// String 打印启用状态
func (em EnableMark) String() string {
if em {
return "开启"
}
return "关闭"
}
type auditHistory struct {
Count int64 `json:"key2"` // 被禁次数
ResList []baiduRes `json:"reslist"` // 禁言原因
}
var bdcli *censor.ContentCensorClient // 百度云审核服务Client
var typetext = [8]string{
0: "默认违禁词库",
1: "违禁违规",
2: "文本色情",
3: "敏感信息",
4: "恶意推广",
5: "低俗辱骂",
6: "恶意推广-联系方式",
7: "恶意推广-软文推广",
} // 文本类型
var (
config keyConfig // 插件配置
configinit bool // 配置初始化
configpath string // 配置路径
)
func init() {
engine := control.Register("baiduaudit", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "##该功能来自百度内容审核需购买相关服务并创建app##\n" +
"- 获取BDAKey\n" +
"- 配置BDAKey [API key] [Secret Key]\n" +
"- 开启/关闭内容审核\n" +
"- 开启/关闭撤回提示\n" +
"- 开启/关闭详细提示\n" +
"- 开启/关闭撤回禁言\n" +
"##禁言时间设置## 禁言时间计算方式为:禁言次数*每次禁言累加时间,当达到最大禁言时间时,再次触发按最大禁言时间计算\n" +
"- 开启/关闭禁言累加\n" +
"- 设置撤回禁言时间[分钟,默认:1]\n" +
"- 设置最大禁言时间[分钟,默认:60,最大43200]\n" +
"- 设置每次累加时间[分钟,默认:1]\n" +
"##检测类型设置## 类型编号列表:[1:违禁违规、2:文本色情、3:敏感信息、4:恶意推广、5:低俗辱骂 6:恶意推广-联系方式、7:恶意推广-软文推广]\n" +
"- 查看检测类型\n" +
"- 查看检测配置\n" +
"- 设置检测类型[类型编号]\n" +
"- 设置不检测类型[类型编号]\n" +
"- 开启/关闭文本检测\n" +
"- 开启/关闭图像检测\n" +
"##测试功能##\n" +
"- 测试文本检测[文本内容]\n" +
"- 测试图像检测[图片]\n",
PrivateDataFolder: "baiduaudit",
})
configpath = engine.DataFolder() + "config.json"
loadConfig()
if configinit {
bdcli = censor.NewClient(config.Key1, config.Key2)
}
engine.OnFullMatch("获取BDAKey", zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
ctx.SendChain(message.Text("接口key创建网址:\n" +
"https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/index\n" +
"免费8w次数领取地址:\n" +
"https://console.bce.baidu.com/ai/?_=1665977657185#/ai/antiporn/overview/resource/getFree"))
})
engine.OnRegex("^查看检测(类型|配置)$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
// 获取群配置
group := getGroup(ctx.Event.GroupID)
var msgs string
k1 := ctx.State["regex_matched"].([]string)[1]
if k1 == "类型" {
msgs += "本群检测类型:"
find := false
// 遍历群检测类型名单
for i, v := range group.WhiteListType {
if !v {
find = true
msgs += fmt.Sprint("\n", i, ".", typetext[i])
}
}
if !find {
msgs += "无"
}
} else {
// 生成配置文本
msgs = fmt.Sprintf("本群配置:\n"+
"内容审核:%s\n"+
"-文本:%s\n"+
"-图像:%s\n"+
"撤回提示:%s\n"+
"-详细提示:%s\n"+
"撤回禁言:%s\n"+
"-禁言累加:%s\n"+
"-撤回禁言时间:%v分钟\n"+
"-每次累加时间:%v分钟\n"+
"-最大禁言时间:%v分钟", group.Enable, group.TextAudit, group.ImageAudit, group.DMRemind, group.MoreRemind, group.DMBAN, group.BANTimeAddEnable, group.BANTime, group.BANTimeAddTime, group.MaxBANTimeAddRange)
}
b, err := text.RenderToBase64(msgs, text.FontFile, 300, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Image("base64://" + binary.BytesToString(b)))
})
engine.OnRegex("^设置(不)?检测类型([01234567])$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
defer jsonSave(config, configpath)
k1 := ctx.State["regex_matched"].([]string)[1]
k2 := ctx.State["regex_matched"].([]string)[2]
group := getGroup(ctx.Event.GroupID)
inputType, _ := strconv.Atoi(k2)
if k1 == "不" {
group.WhiteListType[inputType] = true //不检测:则进入类型白名单
} else {
group.WhiteListType[inputType] = false //检测:则退出白名单
}
config.Groups[ctx.Event.GroupID] = group
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群将%s检测%s类型内容", k1, typetext[inputType])))
})
engine.OnRegex("^设置(最大|每次|撤回)(累加|禁言)时间(\\d{1,5})$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
defer jsonSave(config, configpath)
k1 := ctx.State["regex_matched"].([]string)[1]
k3 := ctx.State["regex_matched"].([]string)[3]
group := getGroup(ctx.Event.GroupID)
time, _ := strconv.ParseInt(k1, 10, 64)
switch k1 {
case "最大":
group.MaxBANTimeAddRange = time
case "每次":
group.BANTimeAddTime = time
case "撤回":
group.BANTime = time
}
config.Groups[ctx.Event.GroupID] = group
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群%s禁言累加时间已设置为%s", k3, k1)))
})
engine.OnRegex("^(开启|关闭)(内容审核|撤回提示|撤回禁言|禁言累加|详细提示|文本检测|图像检测)$", zero.AdminPermission, clientCheck).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
defer jsonSave(config, configpath)
k1 := ctx.State["regex_matched"].([]string)[1]
k2 := ctx.State["regex_matched"].([]string)[2]
isEnable := EnableMark(false)
group := getGroup(ctx.Event.GroupID)
if k1 == "开启" {
isEnable = true
}
switch k2 {
case "内容审核":
group.Enable = isEnable
case "撤回提示":
group.DMRemind = isEnable
case "撤回禁言":
group.DMBAN = isEnable
case "禁言累加":
group.BANTimeAddEnable = isEnable
case "详细提示":
group.MoreRemind = isEnable
case "文本检测":
group.TextAudit = isEnable
case "图像检测":
group.ImageAudit = isEnable
}
config.Groups[ctx.Event.GroupID] = group
ctx.SendChain(message.At(ctx.Event.UserID), message.Text(fmt.Sprintf("本群%s已%s", k2, k1)))
})
engine.OnRegex(`^配置BDAKey\s*(.*)\s*(.*)$`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
k1 := ctx.State["regex_matched"].([]string)[1]
k2 := ctx.State["regex_matched"].([]string)[2]
bdcli = censor.NewClient(k1, k2)
config.Key1 = k1
config.Key2 = k2
if bdcli != nil {
jsonSave(config, configpath)
ctx.SendChain(message.Text("配置成功"))
}
})
engine.OnMessage().SetBlock(false).Handle(func(ctx *zero.Ctx) {
group, ok := config.Groups[ctx.Event.GroupID]
// 如果没该配置,或者审核功能未开启直接跳过
if !ok || !bool(group.Enable) {
return
}
for _, elem := range ctx.Event.Message {
switch elem.Type {
case "image":
if !group.ImageAudit {
return
}
res := bdcli.ImgCensorUrl(elem.Data["url"], nil)
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
banCheck(ctx, bdres)
case "text":
if !group.TextAudit {
return
}
res := bdcli.TextCensor(elem.Data["text"])
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
banCheck(ctx, bdres)
}
}
})
engine.OnPrefix("文本检测", clientCheck).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
if bdcli == nil {
ctx.SendChain(message.Text("Key未配置"))
return
}
args := ctx.ExtractPlainText()
res := bdcli.TextCensor(args)
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
group := getGroup(ctx.Event.GroupID)
ctx.SendChain(buildResp(bdres, group)...)
})
engine.OnPrefix("^图像检测", clientCheck).SetBlock(false).
Handle(func(ctx *zero.Ctx) {
var urls []string
for _, elem := range ctx.Event.Message {
if elem.Type == "image" {
if elem.Data["url"] != "" {
urls = append(urls, elem.Data["url"])
}
}
}
if len(urls) == 0 {
return
}
res := bdcli.ImgCensorUrl(urls[0], nil)
bdres, err := jsonToBaiduRes(res)
if err != nil {
ctx.SendChain(message.Text("Error:", bdres.ErrorMsg, "(", bdres.ErrorCode, ")"))
return
}
group := getGroup(ctx.Event.GroupID)
ctx.SendChain(buildResp(bdres, group)...)
})
}
// 禁言检测
func banCheck(ctx *zero.Ctx, bdres baiduRes) {
// 如果返回类型为2不合规0为合规3为疑似
if bdres.ConclusionType == 2 {
// 创建消息ID
mid := message.NewMessageIDFromInteger(ctx.Event.MessageID.(int64))
// 获取群配置
group := getGroup(ctx.Event.GroupID)
// 检测群配置里的不检测类型白名单,忽略掉不检测的违规类型
for i, b := range group.WhiteListType {
if i == bdres.Data[0].SubType && b {
return
}
}
// 生成回复文本
res := buildResp(bdres, group)
// 撤回消息
ctx.DeleteMessage(mid)
// 查看是否启用撤回后禁言
if group.DMBAN {
// 从历史违规记录中获取指定用户
user := group.getUser(ctx.Event.UserID)
// 用户违规次数自增
user.Count++
// 用户违规原因记录
user.ResList = append(user.ResList, bdres)
// 覆写该用户到群违规记录中
group.AuditHistory[ctx.Event.UserID] = user
// 覆写该群信息
config.Groups[ctx.Event.GroupID] = group
// 保存到json
jsonSave(config, configpath)
var bantime int64
// 查看是否开启禁言累加功能,并计算禁言时间
if group.BANTimeAddEnable {
bantime = user.Count * group.BANTimeAddTime * 60
} else {
bantime = group.BANTime * 60
}
//执行禁言
ctx.SetGroupBan(ctx.Event.GroupID, ctx.Event.UserID, bantime)
}
//查看是否开启撤回提示
if group.DMRemind {
res = append(res, message.At(ctx.Event.Sender.ID))
ctx.SendChain(res...)
}
}
}
// 获取群配置
func getGroup(groupID int64) group {
g, ok := config.Groups[groupID]
if ok {
return g
}
if config.Groups == nil {
config.Groups = make(map[int64]group)
}
g = group{
TextAudit: true,
ImageAudit: true,
BANTime: 1,
MaxBANTimeAddRange: 60,
BANTimeAddTime: 1,
WhiteListType: [8]bool{},
AuditHistory: map[int64]auditHistory{},
}
config.Groups[groupID] = g
return g
}
// 从群历史违规记录中获取用户
func (group *group) getUser(userID int64) auditHistory {
audit, ok := group.AuditHistory[userID]
if ok {
return audit
}
// 如果没有用户,则创建一个并返回
if group.AuditHistory == nil {
group.AuditHistory = make(map[int64]auditHistory)
}
audit = auditHistory{0, []baiduRes{}}
group.AuditHistory[userID] = audit
return audit
}
// 客户端是否初始化检测
func clientCheck(ctx *zero.Ctx) bool {
if bdcli == nil {
ctx.SendChain(message.Text("Key未配置"))
return false
}
return true
}
// 加载JSON配置文件
func loadConfig() {
if file.IsExist(configpath) {
data, err := os.OpenFile(configpath, os.O_RDONLY, 0755)
if err != nil {
panic(err)
}
err = json.NewDecoder(data).Decode(&config)
if err != nil {
panic(err)
}
configinit = true
} else {
config = keyConfig{}
configinit = false
}
}
// 保存配置文件
func jsonSave(v keyConfig, path string) {
jsf, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
defer func(file *os.File) {
err := file.Close()
if err != nil {
fmt.Println(err)
}
}(jsf) // 结束时关闭句柄,释放资源
err := json.NewEncoder(jsf).Encode(v)
if err != nil {
fmt.Println(err)
}
}
// JSON反序列化
func jsonToBaiduRes(resjson string) (baiduRes, error) {
var bdres baiduRes
err := json.Unmarshal(binary.StringToBytes(resjson), &bdres)
return bdres, err
}
// 生成回复文本
func buildResp(bdres baiduRes, group group) []message.MessageSegment {
// 建立消息段
msgs := make([]message.MessageSegment, 0, 8)
// 生成简略审核结果回复
msgs = append(msgs, message.Text(bdres.Conclusion, "\n"))
// 查看是否开启详细审核内容提示,并确定审核内容值为疑似,或者不合规
if !group.MoreRemind {
return msgs
}
// 遍历返回的不合规数据,生成详细违规内容
for i, datum := range bdres.Data {
msgs = append(msgs, message.Text("[", i, "]:", datum.Msg, "\n"))
// 检查命中词条是否大于0
if len(datum.Hits) == 0 {
return msgs
}
// 遍历打印命中的违规词条
for _, hit := range datum.Hits {
if len(datum.Hits) == 0 {
return msgs
}
msgs = append(msgs, message.Text("("))
for i4, i3 := range hit.Words {
// 检查是否是最后一个要打印的词条,如果是则不加上逗号
if i4 != len(hit.Words)-1 {
msgs = append(msgs, message.Text(i3, ","))
} else {
msgs = append(msgs, message.Text(i3))
}
}
msgs = append(msgs, message.Text(")"))
}
}
return msgs
}

View File

@@ -1,53 +0,0 @@
package driftbottle
import (
"fmt"
"hash/crc64"
"strconv"
"sync"
"github.com/FloatTech/floatbox/binary"
sql "github.com/FloatTech/sqlite"
)
type bottle struct {
ID int64 `db:"id"` // ID qq_grp_name_msg 的 crc64
QQ int64 `db:"qq"` // QQ 发送者 qq
Grp int64 `db:"grp"` // Grp 限制抽出的群 / 人(负数)
Name string `db:"name"` // Name 发送者 昵称
Msg string `db:"msg"` // Msg 消息,纯文本
}
var sea = &sql.Sqlite{}
var seamu sync.RWMutex
func newBottle(qq, grp int64, name, msg string) *bottle {
id := int64(crc64.Checksum(binary.StringToBytes(fmt.Sprintf("%d_%d_%s_%s", qq, grp, name, msg)), crc64.MakeTable(crc64.ISO)))
return &bottle{ID: id, QQ: qq, Grp: grp, Name: name, Msg: msg}
}
func (b *bottle) throw(db *sql.Sqlite, channel string) error {
seamu.Lock()
defer seamu.Unlock()
return db.Insert(channel, b)
}
func (b *bottle) destroy(db *sql.Sqlite, channel string) error {
seamu.Lock()
defer seamu.Unlock()
return db.Del(channel, "WHERE id="+strconv.FormatInt(b.ID, 10))
}
// fetchBottle grp != 0
func fetchBottle(db *sql.Sqlite, channel string, grp int64) (*bottle, error) {
seamu.RLock()
defer seamu.RUnlock()
b := new(bottle)
return b, db.Find(channel, b, "WHERE grp=0 or grp="+strconv.FormatInt(grp, 10)+" ORDER BY RANDOM() limit 1")
}
func createChannel(db *sql.Sqlite, channel string) error {
seamu.Lock()
defer seamu.Unlock()
return db.Create(channel, &bottle{})
}

View File

@@ -2,132 +2,107 @@
package driftbottle
import (
"fmt"
"hash/crc64"
"strconv"
"strings"
"sync"
"time"
"unicode/utf8"
"github.com/FloatTech/floatbox/binary"
sql "github.com/FloatTech/sqlite"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/sirupsen/logrus"
"github.com/FloatTech/zbputils/ctxext"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)
type sea struct {
ID int64 `db:"id"` // ID qq_grp_name_msg 的 crc64 hashCheck.
QQ int64 `db:"qq"` // Get current user(Who sends this)
Name string `db:"Name"` // his or her name at that time:P
Msg string `db:"msg"` // What he or she sent to bot?
Grp int64 `db:"grp"` // which group sends this msg?
Time string `db:"time"` // we need to know the current time,master>
}
var seaSide = &sql.Sqlite{}
var seaLocker sync.RWMutex
// We need a container to inject what we need :(
func init() {
en := control.Register("driftbottle", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "漂流瓶\n- (在群xxx)丢漂流瓶(到频道xxx) [消息]\n- (从频道xxx)捡漂流瓶\n- @BOT 创建频道 xxx\n- 跳入(频道)海中\n- 注:不显式限制时,私聊发送可在所有群抽到,群聊发送仅可在本群抽到,默认频道为 global",
Help: "简单的漂流瓶\n" + "- @bot pick" + "- @bot throw xxx (xxx为投递内容)",
PrivateDataFolder: "driftbottle",
})
sea.DBPath = en.DataFolder() + "sea.db"
err := sea.Open(time.Hour * 24)
seaSide.DBPath = en.DataFolder() + "sea.db"
err := seaSide.Open(time.Hour * 24)
if err != nil {
panic(err)
}
_ = createChannel(sea, "global")
en.OnRegex(`^(在群\d+)?丢漂流瓶(到频道\w+)?\s+(.*)$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msgs := ctx.State["regex_matched"].([]string)
grp := ctx.Event.GroupID
channel := "global"
msg := msgs[3]
var err error
if msgs[1] != "" {
grp, err = strconv.ParseInt(msgs[1][6:], 10, 64)
if err != nil {
ctx.SendChain(message.Text("群号非法!"))
return
}
}
if msgs[2] != "" {
channel = msgs[2][9:]
}
if msg == "" {
ctx.SendChain(message.Text("消息为空!"))
return
}
logrus.Debugln("[driftbottle]", grp, channel, msg)
err = newBottle(
ctx.Event.UserID,
grp,
ctx.CardOrNickName(ctx.Event.UserID),
msg,
).throw(sea, channel)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你将它扔进大海,希望有人捞到吧~")))
})
en.OnRegex(`^(从频道\w+)?捡漂流瓶$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msgs := ctx.State["regex_matched"].([]string)
grp := ctx.Event.GroupID
if grp == 0 {
grp = -ctx.Event.UserID
}
if grp == 0 {
ctx.SendChain(message.Text("找不到对象!"))
return
}
channel := "global"
if msgs[1] != "" {
channel = msgs[1][9:]
}
logrus.Debugln("[driftbottle]", grp, channel)
b, err := fetchBottle(sea, channel, grp)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
err = b.destroy(sea, channel)
wg.Done()
}()
ctx.Send(
message.ReplyWithMessage(
ctx.Event.MessageID,
message.Text("你在海边捡到了一个来自 ", b.Name, " 的漂流瓶,打开瓶子,里面有一张纸条,写着:"),
message.Text(b.Msg),
),
)
wg.Wait()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
})
en.OnPrefix("创建频道", zero.SuperUserPermission, zero.OnlyToMe).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
channel := strings.TrimRight(ctx.State["args"].(string), " ")
if channel == "" {
ctx.SendChain(message.Text("频道名为空!"))
return
}
err := createChannel(sea, channel)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("成功~")))
})
en.OnRegex(`^跳入(\w+)?海中$`).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msgs := ctx.State["regex_matched"].([]string)
channel := "global"
if msgs[1] != "" {
channel = msgs[1]
}
seamu.RLock()
c, err := sea.Count(channel)
seamu.RUnlock()
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("你缓缓走入大海,感受着海浪轻柔地拍打着你的小腿,膝盖……\n波浪卷着你的腰腹你感觉有些把握不住平衡了……\n……\n你沉入海中", c, " 个物体与你一同沉浮。\n不知何处涌来一股暗流你失去了意识。")))
})
_ = createChannel(seaSide)
en.OnFullMatch("pick", zero.OnlyToMe, zero.OnlyGroup).Limit(ctxext.LimitByGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
be, err := fetchBottle(seaSide)
if err != nil {
ctx.SendChain(message.Text("ERR:", err))
}
idstr := strconv.Itoa(int(be.ID))
qqstr := strconv.Itoa(int(be.QQ))
grpstr := strconv.Itoa(int(be.Grp))
botname := zero.BotConfig.NickName[0]
msg := message.Message{message.CustomNode(botname, ctx.Event.SelfID, botname+"试着帮你捞出来了这个~\nID:"+idstr+"\n投递人: "+be.Name+"("+qqstr+")"+"\n群号: "+grpstr+"\n时间: "+be.Time+"\n内容: \n"+be.Msg)}
ctx.Send(msg)
})
en.OnRegex(`throw.*?(.*)`, zero.OnlyToMe, zero.OnlyGroup).SetBlock(true).Handle(func(ctx *zero.Ctx) {
senderFormatTime := time.Unix(ctx.Event.Time, 0).Format("2006-01-02 15:04:05")
rawSenderMessage := ctx.State["regex_matched"].([]string)[1]
rawMessageCallBack := message.UnescapeCQCodeText(rawSenderMessage)
keyWordsNum := utf8.RuneCountInString(rawMessageCallBack)
if keyWordsNum < 10 {
ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("需要投递的内容过少( "))
return
}
// check current needs and prepare to throw drift_bottle.
err = globalbottle(
ctx.Event.UserID,
ctx.Event.GroupID,
senderFormatTime,
ctx.CardOrNickName(ctx.Event.UserID),
rawMessageCallBack,
).throw(seaSide)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text("已经帮你丢出去了哦~")))
})
}
func globalbottle(qq, grp int64, time, name, msg string) *sea { // Check as if the User is available and collect information to store.
id := int64(crc64.Checksum(binary.StringToBytes(fmt.Sprintf("%d_%d_%s_%s_%s", grp, qq, time, name, msg)), crc64.MakeTable(crc64.ISO)))
return &sea{ID: id, Grp: grp, Time: time, QQ: qq, Name: name, Msg: msg}
}
func (be *sea) throw(db *sql.Sqlite) error {
seaLocker.Lock()
defer seaLocker.Unlock()
return db.Insert("global", be)
}
func fetchBottle(db *sql.Sqlite) (*sea, error) {
seaLocker.Lock()
defer seaLocker.Unlock()
be := new(sea)
return be, db.Pick("global", be)
}
func createChannel(db *sql.Sqlite) error {
seaLocker.Lock()
defer seaLocker.Unlock()
return db.Create("global", &sea{})
}

View File

@@ -165,7 +165,7 @@ func init() { // 插件主体
ctx.SendChain(message.Text(serviceErr, "ERROR:\n", err))
}
})
engine.OnRegex(`^猜歌(开启|关闭)(歌单|歌词)自动下载`).SetBlock(true).
engine.OnRegex(`^猜歌(开启|关闭)(歌单|歌词)自动下载`, zero.SuperUserPermission).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
swtich := ctx.State["regex_matched"].([]string)[1]
option := ctx.State["regex_matched"].([]string)[1]

View File

@@ -43,11 +43,11 @@ func TestSetHoliday(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = SetHoliday("中秋节", 1, 2022, 9, 10)
err = SetHoliday("中秋节", 1, 2023, 9, 29)
if err != nil {
t.Fatal(err)
}
err = SetHoliday("国庆节", 7, 2022, 10, 1)
err = SetHoliday("国庆节", 7, 2023, 10, 1)
if err != nil {
t.Fatal(err)
}

View File

@@ -95,7 +95,8 @@ func init() {
})
engine.OnFullMatch("所有本地setu分类").SetBlock(true).
Handle(func(ctx *zero.Ctx) {
msg := "所有本地setu分类"
msg := "本地setu分类一览"
hasnotchange := true
ns.mu.RLock()
for i, c := range ns.List() {
n, err := ns.db.Count(c)
@@ -105,8 +106,12 @@ func init() {
msg += fmt.Sprintf("\n%02d. %s(error)", i, c)
logrus.Errorln("[nsetu]", err)
}
hasnotchange = false
}
ns.mu.RUnlock()
if hasnotchange {
msg += "\n空"
}
ctx.SendChain(message.Text(msg))
})
}

View File

@@ -25,6 +25,11 @@ func (g *grammar) string() string {
var db = &sql.Sqlite{}
func getRandomGrammarByTag(tag string) (g grammar) {
_ = db.Find("grammar", &g, "where tag LIKE '%"+tag+"%' ORDER BY RANDOM() limit 1")
_ = db.Find("grammar", &g, "WHERE tag LIKE '%"+tag+"%' ORDER BY RANDOM() limit 1")
return
}
func getRandomGrammarByKeyword(keyword string) (g grammar) {
_ = db.Find("grammar", &g, "WHERE (name LIKE '%"+keyword+"%' or pronunciation LIKE '%"+keyword+"%') ORDER BY RANDOM() limit 1")
return
}

View File

@@ -17,7 +17,8 @@ import (
func init() {
engine := control.Register("nihongo", &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "日语学习\n- 日语语法[xxx](使用tag随机)",
Help: "日语学习\n- 日语语法[xxx](使用tag随机)\n" +
"搜索日语语法[xxx]",
PublicDataFolder: "Nihongo",
})
@@ -47,7 +48,7 @@ func init() {
return true
})
engine.OnRegex(`^日语语法\s?([0-9A-Za-zぁ-んァ-ヶ]{1,6})$`, getdb).SetBlock(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 {
@@ -63,4 +64,20 @@ func init() {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
})
engine.OnRegex(`^搜索日语语法\s?([0-9A-Za-zぁ-んァ-ヶ~]{1,25})$`, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
g := getRandomGrammarByKeyword(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: 可能被风控了"))
}
})
}

View File

@@ -61,7 +61,7 @@ func init() {
DisableOnDefault: false,
PrivateDataFolder: "qqwife",
Help: "一群一天一夫一妻制群老婆\n每天凌晨刷新CP\n" +
"- 娶群友\n- 群老婆列表\n- 允许/禁止自由恋爱\n- 允许/禁止牛头人\n- 设置CD为xx小时 →(默认12小时)\n- 重置花名册\n- 重置所有花名册(用于清除所有群数据及其设置)\n" +
"- 娶群友\n- 群老婆列表\n- [允许|禁止]自由恋爱\n- [允许|禁止]牛头人\n- 设置CD为xx小时 →(默认12小时)\n- 重置花名册\n- 重置所有花名册(用于清除所有群数据及其设置)\n" +
"--------------------------------\n以下指令存在CD,不跨天刷新,前两个受指令开关\n--------------------------------\n" +
"- (娶|嫁)@对方QQ\n自由选择对象自由恋爱(好感度越高成功率越高,保底30%概率)\n" +
"- 当[对方Q号|@对方QQ]的小三\n我和你才是真爱为了你我愿意付出一切(好感度越高成功率越高,保底10%概率)\n" +

View File

@@ -147,7 +147,7 @@ func init() { // 插件主体
msg = append(msg, message.Image(pic))
}
msg = append(msg, message.Text("\n图源: ", result.Header.IndexName, binary.BytesToString(b)))
ctx.Send(ctxext.FakeSenderForwardNode(ctx, msg...))
ctx.Send(message.Message{ctxext.FakeSenderForwardNode(ctx, msg...)})
if s > 80.0 {
continue
}

View File

@@ -12,15 +12,16 @@ import (
)
const (
chpURL = "https://api.shadiao.app/chp"
duURL = "https://api.shadiao.app/du"
pyqURL = "https://api.shadiao.app/pyq"
shadiaoURL = "https://api.shadiao.pro"
chpURL = shadiaoURL + "/chp"
duURL = shadiaoURL + "/du"
pyqURL = shadiaoURL + "/pyq"
yduanziURL = "http://www.yduanzi.com/duanzi/getduanzi"
chayiURL = "https://api.lovelive.tools/api/SweetNothings/Web/0"
ganhaiURL = "https://api.lovelive.tools/api/SweetNothings/Web/1"
ergofabulousURL = "https://ergofabulous.org/luther/?"
ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36"
sdReferer = "https://api.shadiao.app/"
sdReferer = shadiaoURL
yduanziReferer = "http://www.yduanzi.com/?utm_source=shadiao.app"
loveliveReferer = "https://lovelive.tools/"
)

View File

@@ -4,14 +4,19 @@ package tarot
import (
"encoding/json"
"math/rand"
"os"
"strconv"
"strings"
"github.com/FloatTech/floatbox/binary"
fcext "github.com/FloatTech/floatbox/ctxext"
"github.com/FloatTech/floatbox/file"
"github.com/FloatTech/floatbox/process"
"github.com/FloatTech/floatbox/web"
ctrl "github.com/FloatTech/zbpctrl"
"github.com/FloatTech/zbputils/control"
"github.com/FloatTech/zbputils/ctxext"
"github.com/FloatTech/zbputils/img/pool"
"github.com/FloatTech/zbputils/img/text"
"github.com/sirupsen/logrus"
zero "github.com/wdvxdr1123/ZeroBot"
@@ -37,12 +42,13 @@ type formation struct {
}
type cardSet = map[string]card
var cardMap = make(cardSet, 80)
var infoMap = make(map[string]cardInfo, 80)
var formationMap = make(map[string]formation, 10)
var majorArcanaName = make([]string, 0, 80)
var formationName = make([]string, 0, 10)
var (
cardMap = make(cardSet, 80)
infoMap = make(map[string]cardInfo, 80)
formationMap = make(map[string]formation, 10)
majorArcanaName = make([]string, 0, 80)
formationName = make([]string, 0, 10)
)
func init() {
engine := control.Register("tarot", &ctrl.Options[*zero.Ctx]{
@@ -55,6 +61,13 @@ func init() {
PublicDataFolder: "Tarot",
}).ApplySingle(ctxext.DefaultSingle)
cache := engine.DataFolder() + "cache"
_ = os.RemoveAll(cache)
err := os.MkdirAll(cache, 0755)
if err != nil {
panic(err)
}
getTarot := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
data, err := engine.GetLazyData("tarots.json", true)
if err != nil {
@@ -131,12 +144,32 @@ func init() {
if p == 1 {
description = card.ReverseDescription
}
if id := ctx.SendChain(
message.Text(reasons[rand.Intn(len(reasons))], position[p], "的『", name, "』\n"),
message.Image(bed+reverse[p]+card.ImgURL),
message.Text("\n其释义为: ", description)); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
imgurl := bed + reverse[p] + card.ImgURL
imgname := ""
if p == 1 {
imgname = reverse[p][:len(reverse[p])-1] + name
} else {
imgname = name
}
imgpath := cache + "/" + imgname + ".png"
err := pool.SendImageFromPool("pool"+imgname, imgpath, func() error {
data, err := web.RequestDataWith(web.NewTLS12Client(), imgurl, "GET", "gitcode.net", web.RandUA())
if err != nil {
return err
}
f, err := os.Create(imgpath)
if err != nil {
return err
}
defer f.Close()
return os.WriteFile(f.Name(), data, 0755)
}, ctxext.Send(ctx), ctxext.GetMessage(ctx))
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text(reasons[rand.Intn(len(reasons))], position[p], "的『", name, "』\n其释义为: ", description))
return
}
msg := make(message.Message, n)
@@ -156,41 +189,61 @@ func init() {
if p == 1 {
description = card.ReverseDescription
}
tarotMsg := message.Message{
message.Text(position[p], "的『", name, "』\n"),
message.Image(bed + reverse[p] + card.ImgURL),
message.Text("\n其释义为: ", description)}
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotMsg...)
imgurl := bed + reverse[p] + card.ImgURL
tarotmsg := message.Message{message.Text(reasons[rand.Intn(len(reasons))], position[p], "的『", name, "』\n")}
var imgmsg message.MessageSegment
var err error
if p == 1 {
imgmsg, err = poolimg(ctx, imgurl, reverse[p][:len(reverse[p])-1]+name, cache)
} else {
imgmsg, err = poolimg(ctx, imgurl, name, cache)
}
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
tarotmsg = append(tarotmsg, imgmsg)
tarotmsg = append(tarotmsg, message.Text("\n其释义为: ", description))
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotmsg...)
}
if id := ctx.Send(msg).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
ctx.Send(msg)
})
engine.OnRegex(`^解塔罗牌\s?(.*)`, getTarot).SetBlock(true).Limit(ctxext.LimitByGroup).Handle(func(ctx *zero.Ctx) {
match := ctx.State["regex_matched"].([]string)[1]
info, ok := infoMap[match]
if ok {
ctx.SendChain(
message.Image(bed+info.ImgURL),
message.Text("\n", match, "的含义是~"),
message.Text("\n『正位』:", info.Description),
message.Text("\n『逆位』:", info.ReverseDescription))
} else {
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)
imgurl := bed + info.ImgURL
var tarotmsg message.Message
imgmsg, err := poolimg(ctx, imgurl, match, cache)
if err != nil {
ctx.SendChain(message.Text("没有找到", match, "噢~"))
ctx.SendChain(message.Text("ERROR: ", err))
return
}
ctx.SendChain(message.Text("没有找到", match, "噢~"), message.Image("base64://"+binary.BytesToString(cardList)))
tarotmsg = append(tarotmsg, imgmsg)
tarotmsg = append(tarotmsg, message.Text("\n", match, "的含义是~\n『正位』:", info.Description, "\n『逆位』:", info.ReverseDescription))
if id := ctx.Send(tarotmsg).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
return
}
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("ERROR: ", err))
return
}
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) {
cardType := ctx.State["regex_matched"].([]string)[1]
@@ -230,7 +283,20 @@ func init() {
if p == 1 {
description = card.ReverseDescription
}
tarotMsg := message.Message{message.Image(bed + reverse[p] + card.ImgURL)}
var tarotmsg message.Message
imgurl := bed + reverse[p] + card.ImgURL
var imgmsg message.MessageSegment
var err error
if p == 1 {
imgmsg, err = poolimg(ctx, imgurl, reverse[p][:len(reverse[p])-1]+name, cache)
} else {
imgmsg, err = poolimg(ctx, imgurl, name, cache)
}
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
tarotmsg = append(tarotmsg, imgmsg)
build.WriteString(info.Represent[0][i])
build.WriteString(":")
build.WriteString(position[p])
@@ -239,18 +305,54 @@ func init() {
build.WriteString("』\n其释义为: \n")
build.WriteString(description)
build.WriteString("\n")
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotMsg...)
msg[i] = ctxext.FakeSenderForwardNode(ctx, tarotmsg...)
}
txt := build.String()
formation, err := text.RenderToBase64(txt, text.FontFile, 400, 20)
formation, err := text.RenderToBase64(txt, text.FontFile, 420, 20)
if err != nil {
ctx.SendChain(message.Text("ERROR: ", err))
return
}
msg[info.CardsNum] = ctxext.FakeSenderForwardNode(ctx, message.Message{message.Image("base64://" + binary.BytesToString(formation))}...)
ctx.Send(msg)
if id := ctx.Send(msg).ID(); id == 0 {
ctx.SendChain(message.Text("ERROR: 可能被风控了"))
}
} else {
ctx.SendChain(message.Text("没有找到", match, "噢~\n现有牌阵列表: \n", strings.Join(formationName, "\n")))
}
})
}
func poolimg(ctx *zero.Ctx, imgurl, imgname, cache string) (msg message.MessageSegment, err error) {
imgfile := cache + "/" + imgname + ".png"
aimgfile := file.BOTPATH + "/" + imgfile
m, err := pool.GetImage("pool" + imgname)
if err == nil {
msg = message.Image(m.String())
if ctxext.SendToSelf(ctx)(msg) == 0 {
msg = msg.Add("cache", "0")
}
return
}
if file.IsNotExist(aimgfile) {
var data []byte
data, err = web.RequestDataWith(web.NewTLS12Client(), imgurl, "GET", "gitcode.net", web.RandUA())
if err != nil {
return
}
var f *os.File
f, err = os.Create(imgfile)
if err != nil {
return
}
defer f.Close()
err = os.WriteFile(f.Name(), data, 0755)
if err != nil {
return
}
}
m.SetFile(aimgfile)
_, _ = m.Push(ctxext.SendToSelf(ctx), ctxext.GetMessage(ctx))
msg = message.Image("file:///" + aimgfile)
return
}

View File

@@ -1,5 +1,5 @@
// Package ernie AI画图
package ernie
// Package wenxin 百度文心AI
package wenxin
import (
"errors"
@@ -19,13 +19,17 @@ import (
// 数据库
sql "github.com/FloatTech/sqlite"
// 百度文心大模型
model "github.com/FloatTech/AnimeAPI/wenxinAI/erniemodle"
// 百度文心AI画图API
wenxin "github.com/FloatTech/AnimeAPI/wenxinAI/ernievilg"
)
const (
serviceName = "AIdraw"
serviceName = "wenxinvilg"
serviceErr = "[" + serviceName + "]ERROR:\n"
modelName = "wenxinmodel"
modelErr = "[" + modelName + "]ERROR:\n"
)
type keydb struct {
@@ -46,24 +50,34 @@ type apikey struct {
}
var (
groupinfo = &keydb{
name = "椛椛"
limit int
vilginfo = &keydb{
db: &sql.Sqlite{},
}
modelinfo = &keydb{
db: &sql.Sqlite{},
}
limit = 50
dtype = [...]string{
"古风", "油画", "水彩画", "卡通画", "二次元", "浮世绘", "蒸汽波艺术", "low poly", "像素风格", "概念艺术", "未来主义", "赛博朋克", "写实风格", "洛丽塔风格", "巴洛克风格", "超现实主义",
}
)
func init() { // 插件主体
go func() {
process.GlobalInitMutex.Lock()
defer process.GlobalInitMutex.Unlock()
name = zero.BotConfig.NickName[0]
}()
engine := control.Register(serviceName, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "AI画图\n" +
Help: "文心AI画图\n" +
"基于百度文心的免费AI画图插件,\n因为是免费的,图片质量你懂的。\n" +
"key申请链接https://wenxin.baidu.com/moduleApi/key\n" +
"注意每个apikey每日上限50次,总上限500次请求。次数超过了请自行更新apikey\n" +
"- 为[自己/本群/QQ号/群+群号]设置AI画图key [API Key] [Secret Key]\n" +
"例:\n[为10086设置AI画图key 123 456]\n[为群10010设置AI画图key 789 101]\n" +
"key申请链接:https://wenxin.baidu.com/moduleApi/key\n" +
"key和erniemodel插件的key相同。\n" +
"注意:每个apikey每日上限50次,总上限500次请求。次数超过了请自行更新apikey\n" +
"- 为[自己/本群/QQ号/群+群号]设置画图key [API Key] [Secret Key]\n" +
"例:\n为自己设置画图key 123 456\n为10086设置画图key 123 456\n为群10010设置画图key 789 101\n" +
"- [bot名称]画几张[图片描述]的[图片类型][图片尺寸]\n" +
"————————————————————\n" +
"图片描述指南:\n图片主体细节词(请用逗号连接)\n官方prompt指南:https://wenxin.baidu.com/wenxin/docs#Ol7ece95m\n" +
@@ -73,8 +87,8 @@ func init() { // 插件主体
"图片尺寸当前只支持:方图/长图/横图\n" +
"————————————————————\n" +
"指令示例:\n" +
"椛椛帮我画几张金凤凰背景绚烂高饱和古风仙境高清4K古风的油画方图",
PrivateDataFolder: "ernievilg",
name + "帮我画几张金凤凰背景绚烂高饱和古风仙境高清4K古风的油画方图",
PrivateDataFolder: "wenxinAI",
}).ApplySingle(single.New(
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
single.WithPostFn[int64](func(ctx *zero.Ctx) {
@@ -87,8 +101,8 @@ func init() { // 插件主体
}),
))
getdb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
groupinfo.db.DBPath = engine.DataFolder() + "keydb.db"
err := groupinfo.db.Open(time.Hour * 24)
vilginfo.db.DBPath = engine.DataFolder() + "ernieVilg.db"
err := vilginfo.db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return false
@@ -101,8 +115,8 @@ func init() { // 插件主体
uid := -ctx.Event.UserID
gid := ctx.Event.GroupID
// 获取个人和群的key
userinfo, err1 := groupinfo.checkGroup(uid)
info, err2 := groupinfo.checkGroup(gid)
userinfo, err1 := vilginfo.checkGroup(uid, "vilg")
info, err2 := vilginfo.checkGroup(gid, "vilg")
switch {
// 如果是个人请求且报错
case gid == 0 && err1 != nil:
@@ -189,7 +203,8 @@ func init() { // 插件主体
return
}
if status == "0" {
msg := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Text("我画好了!"))}
lastTime := time.Duration(i * 10 * int(time.Second))
msg := message.Message{ctxext.FakeSenderForwardNode(ctx, message.Text("我画好了!\n本次绘画用了", lastTime))}
for _, imginfo := range picURL {
msg = append(msg,
ctxext.FakeSenderForwardNode(ctx,
@@ -201,14 +216,14 @@ func init() { // 插件主体
break
}
}
err = groupinfo.update(gid)
err = vilginfo.update(gid, 1)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
}
process.SleepAbout1sTo2s()
ctx.SendChain(message.Text("累死了,今天我最多只能画", info.DayLimit-1, "张图哦"))
})
engine.OnRegex(`^为(群)?(自己|本群|\d+)设置AI画图key\s(.*[^\s$])\s(.+)$`, getdb).SetBlock(true).
engine.OnRegex(`^为(群)?(自己|本群|\d+)设置画图key\s(.*[^\s$])\s(.+)$`, getdb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
mode := ctx.State["regex_matched"].([]string)[1]
user := ctx.State["regex_matched"].([]string)[2]
@@ -238,17 +253,267 @@ func init() { // 插件主体
}
dbID = -uid
}
err := groupinfo.insert(dbID, aKey, sKey)
err := vilginfo.insert(dbID, "vilg", aKey, sKey)
if err != nil {
ctx.SendChain(message.Text(serviceErr, err))
return
}
ctx.SendChain(message.Text("成功!"))
})
/*********************************************************/
en := control.Register(modelName, &ctrl.Options[*zero.Ctx]{
DisableOnDefault: false,
Help: "文心AI文本处理\n" +
"基于百度文心AI的API文本处理\n" +
"key申请链接:https://wenxin.baidu.com/moduleApi/key\n" +
"key和ernievilg插件的key相同。\n" +
"注意:每个apikey每日上限200条,总上限2000条。次数超过了请自行更新apikey\n" +
"- 为[自己/本群/QQ号/群+群号]设置文心key [API Key] [Secret Key]\n" +
"例:\n为自己设置文心key 123 456\n为10086设置文心key 123 456\n为群10010设置文心key 789 101\n" +
"————————————————————\n" +
"- 文心作文 (x字的)[作文题目]\n" +
"————————————————————\n" +
"- 文心提案 (x字的)[文案标题]\n" +
"————————————————————\n" +
"- 文心摘要 (x字的)[文章内容]\n" +
"————————————————————\n" +
"- 文心小说 (x字的)[小说上文]\n" +
"————————————————————\n" +
"- 文心对联 [上联]\n" +
"————————————————————\n" +
"- 文心问答 [问题]" +
"————————————————————\n" +
"- 文心补全 [带“_”的填空题]\n" +
"————————————————————\n" +
"- 文心自定义 [prompt]\n" +
"prompt: [问题描述] [问题类型]:[题目] [解答类型]:[解题必带内容]\n" +
"指令示例:\n" +
"文心自定义 请写出下面这道题的解题过程。\\n题目:养殖场养鸭376只,养鸡的只数比鸭多258只,这个养殖场一共养鸭和鸡多少只?\\n解\n" +
"文心自定义 1+1=?\n" +
"文心自定义 歌曲名:大风车转啊转\\n歌词",
}).ApplySingle(single.New(
single.WithKeyFn(func(ctx *zero.Ctx) int64 { return ctx.Event.GroupID }),
single.WithPostFn[int64](func(ctx *zero.Ctx) {
ctx.Break()
ctx.Send(
message.ReplyWithMessage(ctx.Event.MessageID,
message.Text(zero.BotConfig.NickName[0], "正在给别人编辑,请不要打扰哦"),
),
)
}),
))
getmodeldb := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
modelinfo.db.DBPath = engine.DataFolder() + "ernieModel.db"
err := modelinfo.db.Open(time.Hour * 24)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return false
}
return true
})
en.OnRegex(`^为(群)?(自己|本群|\d+)设置文心key\s(.*[^\s$])\s(.+)$`, getmodeldb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
mode := ctx.State["regex_matched"].([]string)[1]
user := ctx.State["regex_matched"].([]string)[2]
aKey := ctx.State["regex_matched"].([]string)[3]
sKey := ctx.State["regex_matched"].([]string)[4]
dbID := -ctx.Event.UserID // 默认给自己
switch {
case mode != "": // 指定群的话
gid, err := strconv.ParseInt(user, 10, 64)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return
}
dbID = gid
case user == "本群": // 用于本群
gid := ctx.Event.GroupID
if gid == 0 {
ctx.SendChain(message.Text(modelErr, "请指定群聊,或者使用指令;\n为群xxx设置AI画图key xxx xxx"))
return
}
dbID = gid
case user != "自己": // 给别人开key
uid, err := strconv.ParseInt(user, 10, 64)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return
}
dbID = -uid
}
err := modelinfo.insert(dbID, "model", aKey, sKey)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return
}
ctx.SendChain(message.Text("成功!"))
})
var erniemodel = map[string]int{
"作文": 1,
"提案": 2,
"摘要": 3,
"对联": 4,
"问答": 5,
"小说": 6,
"补全": 7,
"自定义": 8}
var erniePrompt = map[string]string{
"作文": "zuowen",
"提案": "adtext",
"摘要": "Summarization",
"对联": "couplet",
"问答": "Dialogue",
"小说": "novel",
"补全": "cloze"}
en.OnRegex(`^文心(作文|提案|摘要|小说)\s?((\d+)字的)?(.*)$`, getmodeldb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
uid := -ctx.Event.UserID
gid := ctx.Event.GroupID
// 获取个人和群的key
userinfo, err1 := modelinfo.checkGroup(uid, "model")
info, err2 := modelinfo.checkGroup(gid, "model")
switch {
// 如果是个人请求且报错
case gid == 0 && err1 != nil:
ctx.SendChain(message.Text(modelErr, err1))
return
// 如果群报错而个人没有,就切换成个人的
case err2 != nil && err1 == nil:
gid = uid
info = userinfo
// 如果都报错就以群为优先级
case err1 != nil && err2 != nil:
ctx.SendChain(message.Text(modelErr, err2))
return
}
// 判断使用次数
check := false
switch {
// 群和个人都没有次数了
case info.DayLimit == 0 && userinfo.DayLimit == 0:
ctx.SendChain(message.Text("今日请求次数已到200次了,明天在玩吧"))
return
// 个人还有次数的话
case info.DayLimit == 0 && userinfo.DayLimit != 0:
check = true
}
switch {
// 群和个人都没有总次数了
case info.MaxLimit == 0 && userinfo.MaxLimit == 0:
ctx.SendChain(message.Text("设置的key使用次数超过了限额,请更换key。"))
return
// 个人还有总次数的话
case info.MaxLimit == 0 && userinfo.MaxLimit != 0:
check = true
}
if check { // 如果只有个人有次数就切换回个人key
gid = uid
info = userinfo
}
// 调用API
modelStr := ctx.State["regex_matched"].([]string)[1]
mun := ctx.State["regex_matched"].([]string)[3]
minlen := 1
maxlen := 128
if mun != "" {
max, err := strconv.Atoi(mun)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return
}
minlen = max
if max > 128 {
maxlen = max
}
}
keyword := ctx.State["regex_matched"].([]string)[4]
if len([]rune(keyword)) >= 1000 { // 描述不能超过1000
ctx.SendChain(message.Text("是你写作文还是我写?减少点!"))
return
}
result, err := model.GetResult(info.Token, erniemodel[modelStr], keyword, minlen, maxlen, erniePrompt[modelStr])
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return
}
if id := ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(keyword, "", result))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 请求超时!"))
}
err = modelinfo.update(gid, 1)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
}
})
en.OnRegex(`^文心(对联|问答|补全|自定义)\s?(.*)$`, getmodeldb).SetBlock(true).
Handle(func(ctx *zero.Ctx) {
uid := -ctx.Event.UserID
gid := ctx.Event.GroupID
// 获取个人和群的key
userinfo, err1 := modelinfo.checkGroup(uid, "model")
info, err2 := modelinfo.checkGroup(gid, "model")
switch {
// 如果是个人请求且报错
case gid == 0 && err1 != nil:
ctx.SendChain(message.Text(modelErr, err1))
return
// 如果群报错而个人没有,就切换成个人的
case err2 != nil && err1 == nil:
gid = uid
info = userinfo
// 如果都报错就以群为优先级
case err1 != nil && err2 != nil:
ctx.SendChain(message.Text(modelErr, err2))
return
}
// 判断使用次数
check := false
switch {
// 群和个人都没有次数了
case info.DayLimit == 0 && userinfo.DayLimit == 0:
ctx.SendChain(message.Text("今日请求次数已到200次了,明天在玩吧"))
return
// 个人还有次数的话
case info.DayLimit == 0 && userinfo.DayLimit != 0:
check = true
}
switch {
// 群和个人都没有总次数了
case info.MaxLimit == 0 && userinfo.MaxLimit == 0:
ctx.SendChain(message.Text("设置的key使用次数超过了限额,请更换key。"))
return
// 个人还有总次数的话
case info.MaxLimit == 0 && userinfo.MaxLimit != 0:
check = true
}
if check { // 如果只有个人有次数就切换回个人key
gid = uid
info = userinfo
}
// 创建任务
modelStr := ctx.State["regex_matched"].([]string)[1]
keyword := ctx.State["regex_matched"].([]string)[2]
if len([]rune(keyword)) >= 1000 { // 描述不能超过1000
ctx.SendChain(message.Text("你在写作文吗?减少点!"))
return
}
result, err := model.GetResult(info.Token, erniemodel[modelStr], keyword, 1, 128, erniePrompt[modelStr])
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
return
}
if id := ctx.Send(message.ReplyWithMessage(ctx.Event.MessageID, message.Text(result))); id.ID() == 0 {
ctx.SendChain(message.Text("ERROR: 请求超时!"))
}
err = modelinfo.update(gid, 1)
if err != nil {
ctx.SendChain(message.Text(modelErr, err))
}
})
}
// 登记group的key
func (sql *keydb) insert(gid int64, akey, skey string) error {
func (sql *keydb) insert(gid int64, model, akey, skey string) error {
sql.Lock()
defer sql.Unlock()
// 给db文件创建表格(没有才创建)表格名称groupinfo表格结构apikey
@@ -270,7 +535,12 @@ func (sql *keydb) insert(gid int64, akey, skey string) error {
ID: gid,
APIKey: akey,
SecretKey: skey,
MaxLimit: 500,
}
switch model {
case "vilg":
groupinfo.MaxLimit = 500
case "model":
groupinfo.MaxLimit = 2000
}
}
return sql.db.Insert("groupinfo", &groupinfo)
@@ -278,12 +548,19 @@ func (sql *keydb) insert(gid int64, akey, skey string) error {
// 进行更新
groupinfo.APIKey = akey
groupinfo.SecretKey = skey
groupinfo.MaxLimit = 500
groupinfo.Token = ""
groupinfo.Updatetime = 0
switch model {
case "vilg":
groupinfo.MaxLimit = 500
case "model":
groupinfo.MaxLimit = 2000
}
return sql.db.Insert("groupinfo", &groupinfo)
}
// 获取group信息
func (sql *keydb) checkGroup(gid int64) (groupinfo apikey, err error) {
func (sql *keydb) checkGroup(gid int64, model string) (groupinfo apikey, err error) {
sql.Lock()
defer sql.Unlock()
// 给db文件创建表格(没有才创建)表格名称groupinfo表格结构apikey
@@ -291,12 +568,20 @@ func (sql *keydb) checkGroup(gid int64) (groupinfo apikey, err error) {
if err != nil {
return
}
switch model {
case "vilg":
limit = 50
model = "画图"
case "model":
limit = 200
model = "文心"
}
// 先判断该群是否已经设置过key了
if ok := sql.db.CanFind("groupinfo", "where ID is "+strconv.FormatInt(gid, 10)); !ok {
if gid > 0 {
err = errors.New("该群没有设置过apikey请前往https://wenxin.baidu.com/moduleApi/key获取key值后发送指令:\n为本群设置AI画图key [API Key] [Secret Key]")
err = errors.New("该群没有设置过apikey请前往https://wenxin.baidu.com/moduleApi/key获取key值后发送指令:\n为本群设置" + model + "key [API Key] [Secret Key]\n或\n为自己设置" + model + "key [API Key] [Secret Key]")
} else {
err = errors.New("你没有设置过apikey请前往https://wenxin.baidu.com/moduleApi/key获取key值后发送指令:\n为自己设置AI画图key [API Key] [Secret Key]")
err = errors.New("你没有设置过apikey请前往https://wenxin.baidu.com/moduleApi/key获取key值后发送指令:\n为自己设置" + model + "key [API Key] [Secret Key]")
}
return
}
@@ -314,7 +599,7 @@ func (sql *keydb) checkGroup(gid int64) (groupinfo apikey, err error) {
return
}
// 如果token有效期过期
if time.Since(time.Unix(groupinfo.Updatetime, 0)).Hours() > 24 {
if time.Since(time.Unix(groupinfo.Updatetime, 0)).Hours() > 24 || groupinfo.Token == "" {
token, err1 := wenxin.GetToken(groupinfo.APIKey, groupinfo.SecretKey)
if err1 != nil {
err = err1
@@ -350,8 +635,8 @@ func (sql *keydb) checkGroup(gid int64) (groupinfo apikey, err error) {
return
}
// 记录次数(-1)
func (sql *keydb) update(gid int64) error {
// 记录次数(-sub)
func (sql *keydb) update(gid int64, sub int) error {
sql.Lock()
defer sql.Unlock()
// 给db文件创建表格(没有才创建)表格名称groupinfo表格结构apikey
@@ -365,8 +650,8 @@ func (sql *keydb) update(gid int64) error {
if err != nil {
return err
}
groupinfo.MaxLimit--
groupinfo.DayLimit--
groupinfo.MaxLimit -= sub
groupinfo.DayLimit -= sub
err = sql.db.Insert("groupinfo", &groupinfo)
if err != nil {
return err