mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-18 20:50:12 +08:00
feat(aiimage&aichat): add new plugin & summary of group chat (#1187)
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
Some checks failed
自动更新 nix 依赖 / gomod2nix update (push) Has been cancelled
打包最新版为 Docker Image / build docker (push) Has been cancelled
最新版 / Build binary CI (386, linux) (push) Has been cancelled
最新版 / Build binary CI (386, windows) (push) Has been cancelled
最新版 / Build binary CI (amd64, linux) (push) Has been cancelled
最新版 / Build binary CI (amd64, windows) (push) Has been cancelled
最新版 / Build binary CI (arm, linux) (push) Has been cancelled
最新版 / Build binary CI (arm64, linux) (push) Has been cancelled
PushLint / lint (push) Has been cancelled
This commit is contained in:
parent
2fa7868838
commit
34f3b9ba2a
20
README.md
20
README.md
@ -412,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>
|
||||
@ -1496,7 +1508,7 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
|
||||
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/word_count"`
|
||||
|
||||
- [x] 热词 [群号] [消息数目]|热词 123456 1000
|
||||
- [x] 热词 [消息数目]|热词 1000
|
||||
|
||||
</details>
|
||||
<details>
|
||||
@ -1612,9 +1624,9 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
- [x] 设置AI聊天温度80
|
||||
- [x] 设置AI聊天接口类型[OpenAI|OLLaMA|GenAI]
|
||||
- [x] 设置AI聊天(不)支持系统提示词
|
||||
- [x] 设置AI聊天接口地址https://api.deepseek.com/chat/completions
|
||||
- [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
|
||||
@ -1624,6 +1636,8 @@ print("run[CQ:image,file="+j["img"]+"]")
|
||||
- [x] 设置AI聊天TopP 0.9
|
||||
- [x] 设置AI聊天(不)以AI语音输出
|
||||
- [x] 查看AI聊天配置
|
||||
- [x] 重置AI聊天
|
||||
- [x] 群聊总结 [消息数目]|群聊总结 1000
|
||||
|
||||
</details>
|
||||
<details>
|
||||
|
||||
6
go.mod
6
go.mod
@ -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.20250614165821-95cf57cf2434
|
||||
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-20250601112706-0175c95164c1
|
||||
github.com/fumiama/deepinfra v0.0.0-20250812083039-f1b27f21d8c9
|
||||
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.20250707133321-6197b8ee5df7
|
||||
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
|
||||
|
||||
12
go.sum
12
go.sum
@ -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.20250614165821-95cf57cf2434 h1:oEYQFQ2/qx10FtZKCNbW3Ohj/Iw71aM4RWpIu+LMmf8=
|
||||
github.com/FloatTech/zbputils v1.7.2-0.20250614165821-95cf57cf2434/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-20250601112706-0175c95164c1 h1:qE3l/y4Y1gMD2NokQ5Nw4NIUjL8ZwYLPIHOExQNu4hM=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250601112706-0175c95164c1/go.mod h1:wW05PQSn8mo1mZIoa6LBUE+3xIBjkoONvnfPTV5ZOhY=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250812083039-f1b27f21d8c9 h1:X2h8RnCgC04LmwBoizYbFawXh/h6CouXmhYtaVuUn7k=
|
||||
github.com/fumiama/deepinfra v0.0.0-20250812083039-f1b27f21d8c9/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=
|
||||
@ -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.20250707133321-6197b8ee5df7 h1:ya+lVbCC/EN5JumpQDDlVCSrWzLwHl4CHzlTANKDvrU=
|
||||
github.com/wdvxdr1123/ZeroBot v1.8.2-0.20250707133321-6197b8ee5df7/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=
|
||||
|
||||
1
main.go
1
main.go
@ -67,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识别
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
// Package aichat OpenAI聊天
|
||||
// Package aichat OpenAI聊天和群聊总结
|
||||
package aichat
|
||||
|
||||
import (
|
||||
"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"
|
||||
@ -18,6 +20,7 @@ import (
|
||||
ctrl "github.com/FloatTech/zbpctrl"
|
||||
"github.com/FloatTech/zbputils/chat"
|
||||
"github.com/FloatTech/zbputils/control"
|
||||
"github.com/FloatTech/zbputils/ctxext"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -30,9 +33,9 @@ var (
|
||||
"- 设置AI聊天温度80\n" +
|
||||
"- 设置AI聊天接口类型[OpenAI|OLLaMA|GenAI]\n" +
|
||||
"- 设置AI聊天(不)支持系统提示词\n" +
|
||||
"- 设置AI聊天接口地址https://api.deepseek.com/chat/completions\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" +
|
||||
@ -41,7 +44,9 @@ var (
|
||||
"- 设置AI聊天最大长度4096\n" +
|
||||
"- 设置AI聊天TopP 0.9\n" +
|
||||
"- 设置AI聊天(不)以AI语音输出\n" +
|
||||
"- 查看AI聊天配置\n",
|
||||
"- 查看AI聊天配置\n" +
|
||||
"- 重置AI聊天\n" +
|
||||
"- 群聊总结 [消息数目]|群聊总结 1000\n",
|
||||
PrivateDataFolder: "aichat",
|
||||
})
|
||||
)
|
||||
@ -53,6 +58,7 @@ var (
|
||||
"GenAI": 2,
|
||||
}
|
||||
apilist = [3]string{"OpenAI", "OLLaMA", "GenAI"}
|
||||
limit = ctxext.NewLimiterManager(time.Second*30, 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -305,4 +311,93 @@ func init() {
|
||||
}
|
||||
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("少女思考中..."))
|
||||
p, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
|
||||
if p > 1000 {
|
||||
p = 1000
|
||||
}
|
||||
if p == 0 {
|
||||
p = 200
|
||||
}
|
||||
gid := ctx.Event.GroupID
|
||||
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
|
||||
}
|
||||
|
||||
// 调用大模型API进行摘要
|
||||
summary, err := summarizeMessages(messages)
|
||||
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)
|
||||
|
||||
// 分割总结内容为多段
|
||||
parts := strings.Split(b.String(), "\n\n")
|
||||
msg := make(message.Message, 0, len(parts))
|
||||
for _, part := range parts {
|
||||
if part != "" {
|
||||
msg = append(msg, ctxext.FakeSenderForwardNode(ctx, message.Text(part)))
|
||||
}
|
||||
}
|
||||
if len(msg) > 0 {
|
||||
ctx.Send(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// summarizeMessages 调用大模型API进行消息摘要
|
||||
func summarizeMessages(messages []string) (string, error) {
|
||||
// 使用现有的AI配置进行摘要
|
||||
x := deepinfra.NewAPI(cfg.API, cfg.Key)
|
||||
mod := model.NewOpenAI(
|
||||
cfg.ModelName, cfg.Separator,
|
||||
float32(70)/100, 0.9, 4096,
|
||||
)
|
||||
|
||||
// 构造摘要请求提示
|
||||
summaryPrompt := "请总结这个群聊内容,要求按发言顺序梳理,明确标注每个发言者的昵称,并完整呈现其核心观点、提出的问题、发表的看法或做出的回应,确保不遗漏关键信息,且能体现成员间的对话逻辑和互动关系:\n\n" +
|
||||
strings.Join(messages, "\n---\n")
|
||||
|
||||
data, err := x.Request(mod.User(summaryPrompt))
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -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).
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user