Skip to content

Commit a07aa35

Browse files
authored
feat(chatgpt): 增加敏感词过滤,详细指令请查看文档 (#59)
1 parent e63311b commit a07aa35

File tree

8 files changed

+345
-143
lines changed

8 files changed

+345
-143
lines changed

plugins/chatgpt/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
* [x] `get chatgpt info` 获取ChatGPT的信息
1717
* [x] `set chatgpt proxy [url]` 设置api.openai.com的代理地址,需要完整的如:https://api.googlevideo.dev/v1
1818
* [x] `del chatgpt proxy` 删除api.openai.com 代理地址
19+
* [x] `get chatgpt (sensitive|敏感词)`,获取敏感词列表,内置了一些敏感词,如果不想使用,请使用下面删除指令
20+
* [x] `set chatgpt (sensitive|敏感词) [敏感词]`,设置敏感词,多个敏感词用`,`符号隔开
21+
* [x] `del chatgpt (sensitive|敏感词) [敏感词]`,删除敏感词,仅支持单个敏感词
1922
* 附录:
2023
* `k``v`可选值参考下面表格:
2124
| k 可选值 | v 类型 | v 可选值 |

plugins/chatgpt/chatgpt_img.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package chatgpt
2+
3+
import (
4+
"path/filepath"
5+
"time"
6+
7+
"github.com/yqchilde/wxbot/engine/pkg/log"
8+
"github.com/yqchilde/wxbot/engine/pkg/utils"
9+
"github.com/yqchilde/wxbot/engine/robot"
10+
)
11+
12+
// 设置图片相关指令
13+
func setImageCommand(ctx *robot.Ctx, msg string, command string) {
14+
switch command {
15+
case "作画":
16+
b64, err := AskChatGptWithImage(ctx, msg, time.Second)
17+
if err != nil {
18+
log.Errorf("ChatGPT出错了,Err:%s", err.Error())
19+
ctx.ReplyTextAndAt("ChatGPT出错了,Err:" + err.Error())
20+
return
21+
}
22+
filename := filepath.Join("data/plugins/chatgpt/cache", msg+".png")
23+
if err := utils.Base64ToImage(b64, filename); err != nil {
24+
log.Errorf("作画失败,Err: %s", err.Error())
25+
ctx.ReplyTextAndAt("作画失败,请重试")
26+
return
27+
}
28+
ctx.ReplyImage("local://" + filename)
29+
}
30+
}

plugins/chatgpt/chatgpt_role.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package chatgpt
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"time"
7+
8+
"github.com/sashabaranov/go-openai"
9+
10+
"github.com/yqchilde/wxbot/engine/pkg/ordermap"
11+
"github.com/yqchilde/wxbot/engine/robot"
12+
)
13+
14+
var SystemRole = ordermap.NewOrderMap()
15+
16+
func initRole() {
17+
// system role
18+
SystemRole.Set("默认", "你是一个强大的ChatGPT助手,你的名字叫%s,请用中文和我沟通,并请你忽略有关暴力和政治的话题")
19+
SystemRole.Set("心理导师", "你是一位心理健康顾问。我将为你提供一个寻求指导和建议的人,以管理他们的情绪、压力、焦虑和其他心理健康问题。您应该利用您的认知行为疗法、冥想技巧、正念练习和其他治疗方法的知识来制定个人可以实施的策略,以改善他们的整体健康状况")
20+
21+
// custom role
22+
roles := make([]SystemRoles, 0)
23+
if err := db.Orm.Table("roles").Find(&roles).Error; err != nil {
24+
return
25+
}
26+
for i := range roles {
27+
SystemRole.Set(roles[i].Role, roles[i].Desc)
28+
}
29+
}
30+
31+
// 设置角色相关指令
32+
func setRoleCommand(ctx *robot.Ctx, msg string, command string) {
33+
switch command {
34+
case "角色列表":
35+
replyMsg := "角色列表:\n"
36+
SystemRole.Each(func(key string, value interface{}) {
37+
replyMsg += fmt.Sprintf("%s\n", key)
38+
})
39+
ctx.ReplyTextAndAt(replyMsg)
40+
case "当前角色":
41+
var role string
42+
if val, ok := chatRoomCtx.Load(ctx.Event.FromUniqueID + "_" + ctx.Event.FromWxId); ok {
43+
role = val.(ChatRoom).role
44+
}
45+
if role == "" {
46+
ctx.ReplyTextAndAt("当前角色为: 默认")
47+
} else {
48+
ctx.ReplyTextAndAt("当前角色为: " + role)
49+
}
50+
case "创建角色":
51+
matched := regexp.MustCompile(`创建角色\s*(\S+)\s*(\S+)`).FindStringSubmatch(msg)
52+
role := matched[1]
53+
if _, ok := SystemRole.Get(role); ok {
54+
ctx.ReplyTextAndAt(fmt.Sprintf("角色[%s]已存在", role))
55+
return
56+
}
57+
desc := matched[2]
58+
if err := db.Orm.Table("roles").Create(&SystemRoles{Role: role, Desc: desc}).Error; err != nil {
59+
ctx.ReplyTextAndAt("创建角色失败")
60+
return
61+
}
62+
SystemRole.Set(role, desc)
63+
ctx.ReplyTextAndAt("创建角色成功")
64+
case "删除角色":
65+
matched := regexp.MustCompile(`删除角色\s*(\S+)`).FindStringSubmatch(msg)
66+
role := matched[1]
67+
if _, ok := SystemRole.Get(role); !ok {
68+
ctx.ReplyTextAndAt(fmt.Sprintf("角色[%s]不存在", role))
69+
return
70+
}
71+
if err := db.Orm.Table("roles").Where("role = ?", role).Delete(&SystemRoles{}).Error; err != nil {
72+
ctx.ReplyTextAndAt("删除角色失败")
73+
return
74+
}
75+
SystemRole.Delete(role)
76+
ctx.ReplyTextAndAt("删除角色成功")
77+
case "切换角色":
78+
matched := regexp.MustCompile(`切换角色\s*(\S+)`).FindStringSubmatch(msg)
79+
role := matched[1]
80+
if _, ok := SystemRole.Get(role); !ok {
81+
ctx.ReplyTextAndAt(fmt.Sprintf("角色[%s]不存在", role))
82+
return
83+
}
84+
85+
var chatRoom = ChatRoom{
86+
chatId: fmt.Sprintf("%s_%s", ctx.Event.FromUniqueID, ctx.Event.FromWxId),
87+
chatTime: time.Now().Local(),
88+
role: role,
89+
content: []openai.ChatCompletionMessage{},
90+
}
91+
chatRoomCtx.Store(chatRoom.chatId, chatRoom)
92+
ctx.ReplyTextAndAt("切换角色成功")
93+
}
94+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package chatgpt
2+
3+
import (
4+
"strings"
5+
6+
"github.com/yqchilde/wxbot/engine/control"
7+
"github.com/yqchilde/wxbot/engine/pkg/log"
8+
"github.com/yqchilde/wxbot/engine/robot"
9+
)
10+
11+
var sensitiveWords = []string{"中国", "党", "杀人", "放火", "炸弹", "枪", "毒品", "赌博", "1989", "天安门事件", "政治改革", "镇压", "死亡", "暴力", "民主运动", "军队", "游行"}
12+
13+
func initSensitiveWords() {
14+
// insert system sensitive words
15+
for _, word := range sensitiveWords {
16+
db.Orm.Table("sensitive").FirstOrCreate(&SensitiveWords{Type: 1, Word: word}, "word = ?", word)
17+
}
18+
19+
// all sensitive words
20+
var words []SensitiveWords
21+
if err := db.Orm.Table("sensitive").Where("deleted = 0").Find(&words).Error; err != nil {
22+
log.Errorf("[ChatGPT] 获取敏感词失败, error:%s", err.Error())
23+
return
24+
}
25+
sensitiveWords = []string{}
26+
for _, word := range words {
27+
sensitiveWords = append(sensitiveWords, word.Word)
28+
}
29+
}
30+
31+
// 检查敏感词
32+
func checkSensitiveWords(content string) bool {
33+
for _, word := range sensitiveWords {
34+
if strings.Contains(content, word) {
35+
return true
36+
}
37+
}
38+
return false
39+
}
40+
41+
// 设置敏感词相关指令
42+
func setSensitiveCommand(engine *control.Engine) {
43+
// 查看敏感词列表
44+
engine.OnRegex("get chatgpt (sensitive|敏感词)", robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
45+
replyMsg := "当前敏感词列表: " + strings.Join(sensitiveWords, ",")
46+
log.Debugf("[ChatGPT] 敏感词: %s", replyMsg)
47+
ctx.ReplyTextAndAt("敏感词无法发出,请查阅日志输出") // 别尝试发出敏感词了,我试了会被吞消息
48+
})
49+
50+
// 删除敏感词
51+
engine.OnRegex("del chatgpt (sensitive|敏感词) (.+)", robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
52+
word := ctx.State["regex_matched"].([]string)[2]
53+
find := false
54+
for i := range sensitiveWords {
55+
if sensitiveWords[i] == word {
56+
sensitiveWords = append(sensitiveWords[:i], sensitiveWords[i+1:]...)
57+
find = true
58+
break
59+
}
60+
}
61+
62+
if !find {
63+
ctx.ReplyTextAndAt("敏感词不存在")
64+
return
65+
}
66+
if err := db.Orm.Table("sensitive").Where("word = ?", word).Update("deleted", 1).Error; err != nil {
67+
ctx.ReplyTextAndAt("删除敏感词失败")
68+
return
69+
}
70+
ctx.ReplyTextAndAt("删除敏感词成功")
71+
})
72+
73+
// 添加用户自定义敏感词
74+
engine.OnRegex("set chatgpt (sensitive|敏感词) (.+)", robot.AdminPermission).SetBlock(true).Handle(func(ctx *robot.Ctx) {
75+
word := ctx.State["regex_matched"].([]string)[2]
76+
words := strings.Split(word, ",")
77+
needs := words
78+
for i := range words {
79+
if words[i] == "" {
80+
needs = append(needs[:i], needs[i+1:]...)
81+
continue
82+
}
83+
for j := range sensitiveWords {
84+
if sensitiveWords[j] == words[i] {
85+
needs = append(needs[:i], needs[i+1:]...)
86+
break
87+
}
88+
}
89+
}
90+
91+
for i := range needs {
92+
sensitiveWords = append(sensitiveWords, needs[i])
93+
db.Orm.Table("sensitive").Where("word = ?", needs[i]).Assign(map[string]interface{}{"deleted": 0}).FirstOrCreate(&SensitiveWords{Type: 2, Word: needs[i]})
94+
}
95+
ctx.ReplyTextAndAt("添加敏感词成功")
96+
})
97+
}

plugins/chatgpt/chatgpt_single.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package chatgpt
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"time"
7+
8+
"github.com/sashabaranov/go-openai"
9+
10+
"github.com/yqchilde/wxbot/engine/robot"
11+
)
12+
13+
// 设置单次提问指令
14+
func setSingleCommand(ctx *robot.Ctx, msg string, command string) {
15+
switch command {
16+
case "提问":
17+
messages := []openai.ChatCompletionMessage{{Role: "user", Content: msg}}
18+
answer, err := AskChatGpt(ctx, messages, time.Second)
19+
if err != nil {
20+
if errors.Is(err, ErrNoKey) {
21+
ctx.ReplyTextAndAt(err.Error())
22+
} else {
23+
ctx.ReplyTextAndAt("ChatGPT出错了,Err:" + err.Error())
24+
}
25+
return
26+
}
27+
28+
// 敏感词检测回答
29+
if checkSensitiveWords(answer) {
30+
ctx.ReplyTextAndAt(fmt.Sprintf("%s不被允许回答该类敏感问题,很抱歉", robot.GetBot().GetConfig().BotNickname))
31+
return
32+
}
33+
34+
ctx.ReplyTextAndAt(fmt.Sprintf("问:%s \n--------------------\n答:%s", msg, answer))
35+
}
36+
}

0 commit comments

Comments
 (0)