Skip to content

Commit 735c3cd

Browse files
committed
feat: add plugin aichat
1 parent 24955dc commit 735c3cd

File tree

6 files changed

+258
-0
lines changed

6 files changed

+258
-0
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,17 @@ print("run[CQ:image,file="+j["img"]+"]")
15371537

15381538
### *低优先级*
15391539

1540+
<details>
1541+
<summary>OpenAI聊天</summary>
1542+
1543+
`import _ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat"`
1544+
1545+
- [x] 设置AI聊天触发概率10
1546+
- [x] 设置AI聊天密钥xxx
1547+
- [x] 设置AI聊天模型名xxx
1548+
- [x] 设置AI聊天系统提示词xxx
1549+
1550+
</details>
15401551
<details>
15411552
<summary>骂人</summary>
15421553

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
github.com/disintegration/imaging v1.6.2
2222
github.com/fumiama/ahsai v0.1.0
2323
github.com/fumiama/cron v1.3.0
24+
github.com/fumiama/deepinfra v0.0.0-20250214072937-12ba46058885
2425
github.com/fumiama/go-base16384 v1.7.0
2526
github.com/fumiama/go-registry v0.2.7
2627
github.com/fumiama/gotracemoe v0.0.3

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ github.com/fumiama/ahsai v0.1.0 h1:LXD61Kaj6kJHa3AEGsLIfKNzcgaVxg7JB72OR4yNNZ4=
5757
github.com/fumiama/ahsai v0.1.0/go.mod h1:fFeNnqgo44i8FIaguK659aQryuZeFy+4klYLQu/rfdk=
5858
github.com/fumiama/cron v1.3.0 h1:ZWlwuexF+HQHl3cYytEE5HNwD99q+3vNZF1GrEiXCFo=
5959
github.com/fumiama/cron v1.3.0/go.mod h1:bz5Izvgi/xEUI8tlBN8BI2jr9Moo8N4or0KV8xXuPDY=
60+
github.com/fumiama/deepinfra v0.0.0-20250214072937-12ba46058885 h1:AHuorF/H+9q/+A3CclMbr5W+kbpaMw1r5E4UUC7ETUQ=
61+
github.com/fumiama/deepinfra v0.0.0-20250214072937-12ba46058885/go.mod h1:pNn32xTo/u72cTCIq3EejJQPTZqg420Xb3XI+Ou7ZmU=
6062
github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
6163
github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
6264
github.com/fumiama/go-registry v0.2.7 h1:tLEqgEpsiybQMqBv0dLHm5leia/z1DhajMupwnOHeNs=

main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ import (
167167
// vvvvvvvvvvvvvv //
168168
// vvvv //
169169

170+
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/aichat" // AI聊天
171+
170172
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/curse" // 骂人
171173

172174
_ "github.com/FloatTech/ZeroBot-Plugin/plugin/thesaurus" // 词典匹配回复

plugin/aichat/list.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package aichat
2+
3+
import (
4+
"sync"
5+
6+
"github.com/fumiama/deepinfra"
7+
"github.com/fumiama/deepinfra/model"
8+
)
9+
10+
const listcap = 6
11+
12+
type list struct {
13+
mu sync.RWMutex
14+
m map[int64][]string
15+
}
16+
17+
func newlist() list {
18+
return list{
19+
m: make(map[int64][]string, 64),
20+
}
21+
}
22+
23+
func (l *list) add(grp int64, txt string) {
24+
l.mu.Lock()
25+
defer l.mu.Unlock()
26+
msgs, ok := l.m[grp]
27+
if !ok {
28+
msgs = make([]string, 1, listcap)
29+
msgs[0] = txt
30+
l.m[grp] = msgs
31+
return
32+
}
33+
if len(msgs) < cap(msgs) {
34+
msgs = append(msgs, txt)
35+
l.m[grp] = msgs
36+
return
37+
}
38+
copy(msgs[:], msgs[1:])
39+
msgs[len(msgs)-1] = txt
40+
l.m[grp] = msgs
41+
}
42+
43+
func (l *list) body(mn, sysp string, grp int64) deepinfra.Model {
44+
m := model.NewCustom(mn, "", 0.7, 0.9, 1024).System(sysp)
45+
l.mu.RLock()
46+
defer l.mu.RUnlock()
47+
for _, msg := range l.m[grp] {
48+
_ = m.User(msg)
49+
}
50+
return m
51+
}

plugin/aichat/main.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Package aichat OpenAI聊天
2+
package aichat
3+
4+
import (
5+
"math/rand"
6+
"os"
7+
"strconv"
8+
"strings"
9+
"sync/atomic"
10+
"unsafe"
11+
12+
"github.com/fumiama/deepinfra"
13+
"github.com/sirupsen/logrus"
14+
15+
zero "github.com/wdvxdr1123/ZeroBot"
16+
"github.com/wdvxdr1123/ZeroBot/message"
17+
18+
"github.com/FloatTech/floatbox/file"
19+
"github.com/FloatTech/floatbox/process"
20+
ctrl "github.com/FloatTech/zbpctrl"
21+
"github.com/FloatTech/zbputils/control"
22+
)
23+
24+
var (
25+
api *deepinfra.API
26+
en = control.AutoRegister(&ctrl.Options[*zero.Ctx]{
27+
DisableOnDefault: false,
28+
Extra: control.ExtraFromString("aichat"),
29+
Brief: "OpenAI聊天",
30+
Help: "- 设置AI聊天触发概率10\n- 设置AI聊天密钥xxx\n- 设置AI聊天模型名xxx\n- 设置AI聊天系统提示词xxx",
31+
PrivateDataFolder: "aichat",
32+
})
33+
lst = newlist()
34+
)
35+
36+
var (
37+
modelname = "deepseek-ai/DeepSeek-R1"
38+
systemprompt = "你正在QQ群与用户聊天,用户发送了消息。按自己的心情简短思考后,条理清晰地回应**一句话**,禁止回应多句。"
39+
)
40+
41+
func init() {
42+
mf := en.DataFolder() + "model.txt"
43+
sf := en.DataFolder() + "system.txt"
44+
if file.IsExist(mf) {
45+
data, err := os.ReadFile(mf)
46+
if err != nil {
47+
logrus.Warnln("read model", err)
48+
} else {
49+
modelname = string(data)
50+
}
51+
}
52+
if file.IsExist(sf) {
53+
data, err := os.ReadFile(sf)
54+
if err != nil {
55+
logrus.Warnln("read system", err)
56+
} else {
57+
systemprompt = string(data)
58+
}
59+
}
60+
61+
en.OnMessage(func(ctx *zero.Ctx) bool {
62+
txt := ctx.ExtractPlainText()
63+
ctx.State["aichat_txt"] = txt
64+
return txt != ""
65+
}).SetBlock(false).Handle(func(ctx *zero.Ctx) {
66+
lst.add(ctx.Event.GroupID, ctx.State["aichat_txt"].(string))
67+
gid := ctx.Event.GroupID
68+
if gid == 0 {
69+
gid = -ctx.Event.UserID
70+
}
71+
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
72+
if !ok {
73+
return
74+
}
75+
rate := c.GetData(gid)
76+
if !ctx.Event.IsToMe && rand.Intn(100) >= int(rate) {
77+
return
78+
}
79+
key := ""
80+
err := c.GetExtra(&key)
81+
if err != nil {
82+
logrus.Warnln("ERROR: get extra err:", err)
83+
return
84+
}
85+
if key == "" {
86+
logrus.Warnln("ERROR: get extra err: empty key")
87+
return
88+
}
89+
var x deepinfra.API
90+
y := &x
91+
if api == nil {
92+
x = deepinfra.NewAPI(deepinfra.APIDeepInfra, key)
93+
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&api)), unsafe.Pointer(&x))
94+
} else {
95+
y = api
96+
}
97+
data, err := y.Request(lst.body(modelname, systemprompt, gid))
98+
if err != nil {
99+
logrus.Warnln("[niniqun] post err:", err)
100+
return
101+
}
102+
txt := strings.Trim(data, "\n  ")
103+
if len(txt) > 0 {
104+
lst.add(ctx.Event.GroupID, txt)
105+
nick := zero.BotConfig.NickName[rand.Intn(len(zero.BotConfig.NickName))]
106+
txt = strings.ReplaceAll(txt, "{name}", ctx.CardOrNickName(ctx.Event.UserID))
107+
txt = strings.ReplaceAll(txt, "{me}", nick)
108+
id := any(nil)
109+
if ctx.Event.IsToMe {
110+
id = ctx.Event.MessageID
111+
}
112+
for _, t := range strings.Split(txt, "{segment}") {
113+
if t == "" {
114+
continue
115+
}
116+
if id != nil {
117+
id = ctx.SendChain(message.Reply(id), message.Text(t))
118+
} else {
119+
id = ctx.SendChain(message.Text(t))
120+
}
121+
process.SleepAbout1sTo2s()
122+
}
123+
}
124+
})
125+
en.OnPrefix("设置AI聊天触发概率", zero.AdminPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
126+
args := strings.TrimSpace(ctx.State["args"].(string))
127+
if args == "" {
128+
ctx.SendChain(message.Text("ERROR: empty args"))
129+
return
130+
}
131+
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
132+
if !ok {
133+
ctx.SendChain(message.Text("ERROR: no such plugin"))
134+
return
135+
}
136+
r, err := strconv.Atoi(args)
137+
if err != nil {
138+
ctx.SendChain(message.Text("ERROR: parse rate err: ", err))
139+
return
140+
}
141+
gid := ctx.Event.GroupID
142+
if gid == 0 {
143+
gid = -ctx.Event.UserID
144+
}
145+
c.SetData(gid, int64(r&0xff))
146+
ctx.SendChain(message.Text("成功"))
147+
})
148+
en.OnPrefix("设置AI聊天密钥", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
149+
args := strings.TrimSpace(ctx.State["args"].(string))
150+
if args == "" {
151+
ctx.SendChain(message.Text("ERROR: empty args"))
152+
return
153+
}
154+
c, ok := ctx.State["manager"].(*ctrl.Control[*zero.Ctx])
155+
if !ok {
156+
ctx.SendChain(message.Text("ERROR: no such plugin"))
157+
return
158+
}
159+
err := c.SetExtra(&args)
160+
if err != nil {
161+
ctx.SendChain(message.Text("ERROR: ", err))
162+
return
163+
}
164+
})
165+
en.OnPrefix("设置AI聊天模型名", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
166+
args := strings.TrimSpace(ctx.State["args"].(string))
167+
if args == "" {
168+
ctx.SendChain(message.Text("ERROR: empty args"))
169+
return
170+
}
171+
modelname = args
172+
err := os.WriteFile(mf, []byte(args), 0644)
173+
if err != nil {
174+
ctx.SendChain(message.Text("ERROR: ", err))
175+
return
176+
}
177+
})
178+
en.OnPrefix("设置AI聊天系统提示词", zero.OnlyPrivate, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
179+
args := strings.TrimSpace(ctx.State["args"].(string))
180+
if args == "" {
181+
ctx.SendChain(message.Text("ERROR: empty args"))
182+
return
183+
}
184+
systemprompt = args
185+
err := os.WriteFile(sf, []byte(args), 0644)
186+
if err != nil {
187+
ctx.SendChain(message.Text("ERROR: ", err))
188+
return
189+
}
190+
})
191+
}

0 commit comments

Comments
 (0)