diff --git a/README.md b/README.md
index 1442aad6ff..f7535e4ae7 100644
--- a/README.md
+++ b/README.md
@@ -412,6 +412,18 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 设置默认限速为每 m [分钟 | 秒] n 次触发
+
+
+ aiimage
+
+ `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画图 [描述]
+
AIWife
@@ -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
@@ -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
diff --git a/go.mod b/go.mod
index c319db1c9e..3e7cd6dc5d 100644
--- a/go.mod
+++ b/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
diff --git a/go.sum b/go.sum
index d4b9a61146..31ada71b36 100644
--- a/go.sum
+++ b/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=
diff --git a/main.go b/main.go
index 5e9b13e577..117b111aa1 100644
--- a/main.go
+++ b/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识别
diff --git a/plugin/aichat/main.go b/plugin/aichat/main.go
index c8ac1df646..dbb9e9aa80 100644
--- a/plugin/aichat/main.go
+++ b/plugin/aichat/main.go
@@ -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
}
diff --git a/plugin/aiimage/config.go b/plugin/aiimage/config.go
new file mode 100644
index 0000000000..e48ec5c926
--- /dev/null
+++ b/plugin/aiimage/config.go
@@ -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()
+}
diff --git a/plugin/aiimage/main.go b/plugin/aiimage/main.go
new file mode 100644
index 0000000000..519065e92a
--- /dev/null
+++ b/plugin/aiimage/main.go
@@ -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)
+ }
+ })
+}
diff --git a/plugin/chatcount/chatcount.go b/plugin/chatcount/chatcount.go
index a4e1bb41a8..caf88ebeed 100644
--- a/plugin/chatcount/chatcount.go
+++ b/plugin/chatcount/chatcount.go
@@ -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).
diff --git a/plugin/wordcount/main.go b/plugin/wordcount/main.go
index 5e58069fa3..127c7da8db 100644
--- a/plugin/wordcount/main.go
+++ b/plugin/wordcount/main.go
@@ -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 {