Browse Source

feat: 增加敏感词功能 (#204)

二丫讲梵 2 years ago
parent
commit
add42dd774
7 changed files with 59 additions and 0 deletions
  1. 3 0
      README.md
  2. 2 0
      config.example.yml
  3. 6 0
      config/config.go
  4. 1 0
      docker-compose.yml
  5. 8 0
      main.go
  6. 6 0
      pkg/process/process_request.go
  7. 33 0
      public/tools.go

+ 3 - 0
README.md

@@ -229,6 +229,7 @@ $ docker run -itd --name chatgpt -p 8090:8090 \
   -e DEFAULT_MODE="单聊" -e MAX_REQUEST=0 -e PORT=8090 \
   -e SERVICE_URL="你当前服务外网可访问的URL" -e CHAT_TYPE="0" \
   -e ALLOW_GROUPS=a,b -e ALLOW_OUTGOING_GROUPS=a,b -e ALLOW_USERS=a,b -e DENY_USERS=a,b -e VIP_USERS=a,b -e ADMIN_USERS=a,b -e APP_SECRETS="xxx,yyy" \
+  -e SENSITIVE_WORDS="aa,bb" \
   -e AZURE_ON="false" -e AZURE_API_VERSION="" -e AZURE_RESOURCE_NAME="" \
   -e AZURE_DEPLOYMENT_NAME="" -e AZURE_OPENAI_TOKEN="" \
   -e HELP="欢迎使用本工具\n\n你可以查看:[用户指南](https://github.com/eryajf/chatgpt-dingtalk/blob/main/docs/userGuide.md)\n\n这是一个[开源项目](https://github.com/eryajf/chatgpt-dingtalk/)
@@ -493,6 +494,8 @@ vip_users: []
 admin_users: []
 # 钉钉机器人在应用信息中的AppSecret,为了校验回调的请求是否合法,如果留空,将会忽略校验,则该接口将会存在其他人也能随意调用的安全隐患,因此强烈建议配置正确的secret,如果你的服务对接给多个机器人,这里可以配置多个机器人的secret
 app_secrets: []
+# 敏感词,提问时触发,则不允许提问,回答的内容中触发,则以 *** 代替
+sensitive_words: []
 # 帮助信息,放在配置文件,可供自定义
 help: "欢迎使用本工具\n\n你可以查看:[用户指南](https://github.com/eryajf/chatgpt-dingtalk/blob/main/docs/userGuide.md)\n\n这是一个[开源项目](https://github.com/eryajf/chatgpt-dingtalk/),觉得不错你可以来波素质三连."
 

+ 2 - 0
config.example.yml

@@ -42,6 +42,8 @@ vip_users: []
 admin_users: []
 # 钉钉机器人在应用信息中的AppSecret,为了校验回调的请求是否合法,如果留空,将会忽略校验,则该接口将会存在其他人也能随意调用的安全隐患,因此强烈建议配置正确的secret,如果你的服务对接给多个机器人,这里可以配置多个机器人的secret
 app_secrets: []
+# 敏感词,提问时触发,则不允许提问,回答的内容中触发,则以 *** 代替
+sensitive_words: []
 # 帮助信息,放在配置文件,可供自定义
 help: "欢迎使用本工具\n\n你可以查看:[用户指南](https://github.com/eryajf/chatgpt-dingtalk/blob/main/docs/userGuide.md)\n\n这是一个[开源项目](https://github.com/eryajf/chatgpt-dingtalk/),觉得不错你可以来波素质三连."
 

+ 6 - 0
config/config.go

@@ -52,6 +52,8 @@ type Configuration struct {
 	AdminUsers []string `yaml:"admin_users"`
 	// 钉钉机器人在应用信息中的AppSecret,为了校验回调的请求是否合法,如果你的服务对接给多个机器人,这里可以配置多个机器人的secret
 	AppSecrets []string `yaml:"app_secrets"`
+	// 敏感词,提问时触发,则不允许提问,回答的内容中触发,则以 *** 代替
+	SensitiveWords []string `yaml:"sensitive_words"`
 	// 自定义帮助信息
 	Help string `yaml:"help"`
 	// AzureOpenAI 配置
@@ -160,6 +162,10 @@ func LoadConfig() *Configuration {
 		if appSecrets != "" {
 			config.AppSecrets = strings.Split(appSecrets, ",")
 		}
+		sensitiveWords := os.Getenv("SENSITIVE_WORDS")
+		if sensitiveWords != "" {
+			config.SensitiveWords = strings.Split(sensitiveWords, ",")
+		}
 		help := os.Getenv("HELP")
 		if help != "" {
 			config.Help = help

+ 1 - 0
docker-compose.yml

@@ -31,6 +31,7 @@ services:
       ADMIN_USERS: "" # 指定哪些人为此系统的管理员,如果留空,则表示没有人是管理员,如果要限制,则列表中写用户的userid
       # 注意:如果下边的app_secrets为空,以及使用outgoing的方式配置机器人,这两种情况下,都表示没有人是管理员
       APP_SECRETS: "" # 钉钉机器人在应用信息中的AppSecret,为了校验回调的请求是否合法,如果留空,将会忽略校验,则该接口将会存在其他人也能随意调用的安全隐患,因此强烈建议配置正确的secret,如果你的服务对接给多个机器人,这里可以配置多个机器人的secret,比如 "xxxx,yyyy"
+      SENSITIVE_WORDS: "" # 敏感词,提问时触发,则不允许提问,回答的内容中触发,则以 *** 代替
       AZURE_ON: "false" # 是否走Azure OpenAi API, 默认false ,如果为true,则需要配置下边的四个参数
       AZURE_API_VERSION: "" # Azure OpenAi API 版本,比如 "2023-03-15-preview"
       AZURE_RESOURCE_NAME: "" # Azure OpenAi API 资源名称,比如 "openai"

+ 8 - 0
main.go

@@ -48,6 +48,14 @@ func Start() {
 		}
 		// 去除问题的前后空格
 		msgObj.Text.Content = strings.TrimSpace(msgObj.Text.Content)
+		if public.JudgeSensitiveWord(msgObj.Text.Content) {
+			_, err = msgObj.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您提问的问题中包含敏感词汇,请审核自己的对话内容之后再进行!**")
+			if err != nil {
+				logger.Warning(fmt.Errorf("send message error: %v", err))
+				return err
+			}
+			return nil
+		}
 		// 打印钉钉回调过来的请求明细,调试时打开
 		logger.Debug(fmt.Sprintf("dingtalk callback parameters: %#v", msgObj))
 

+ 6 - 0
pkg/process/process_request.go

@@ -146,6 +146,9 @@ func Do(mode string, rmsg *dingbot.ReceiveMsg) error {
 				logger.Error("往MySQL新增数据失败,错误信息:", err)
 			}
 			logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply))
+			if public.JudgeSensitiveWord(reply) {
+				reply = public.SolveSensitiveWord(reply)
+			}
 			// 回复@我的用户
 			_, err = rmsg.ReplyToDingtalk(string(dingbot.TEXT), reply)
 			if err != nil {
@@ -204,6 +207,9 @@ func Do(mode string, rmsg *dingbot.ReceiveMsg) error {
 			// 将当前回答的ID放入缓存
 			public.UserService.SetAnswerID(rmsg.SenderNick, rmsg.GetChatTitle(), aid)
 			logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply))
+			if public.JudgeSensitiveWord(reply) {
+				reply = public.SolveSensitiveWord(reply)
+			}
 			// 回复@我的用户
 			_, err = rmsg.ReplyToDingtalk(string(dingbot.TEXT), reply)
 			if err != nil {

+ 33 - 0
public/tools.go

@@ -9,6 +9,7 @@ import (
 	"os"
 	"strings"
 	"time"
+	"unicode/utf8"
 )
 
 // 将内容写入到文件,如果文件名带路径,则会判断路径是否存在,不存在则创建
@@ -140,3 +141,35 @@ func CheckRequest(ts, sg string) bool {
 	}
 	return false
 }
+
+// JudgeSensitiveWord 判断内容是否包含敏感词
+func JudgeSensitiveWord(s string) bool {
+	if len(Config.SensitiveWords) == 0 {
+		return false
+	}
+	for _, v := range Config.SensitiveWords {
+		if strings.Contains(s, v) {
+			return true
+		}
+	}
+	return false
+}
+
+// SolveSensitiveWord 将敏感词用*号占位
+func SolveSensitiveWord(s string) string {
+	for _, v := range Config.SensitiveWords {
+		if strings.Contains(s, v) {
+			return strings.Replace(s, v, printStars(utf8.RuneCountInString(v)), -1)
+		}
+	}
+	return s
+}
+
+// 将对应敏感词替换为*
+func printStars(num int) string {
+	s := ""
+	for i := 0; i < num; i++ {
+		s += "*"
+	}
+	return s
+}