From 22b8cf4c6a8d79418da0944762f17a38bc0bbef2 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Wed, 7 Dec 2022 16:33:24 +0800 Subject: [PATCH 01/37] =?UTF-8?q?=E4=BC=98=E5=8C=96gtp=E8=AF=B7=E6=B1=82?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E8=AF=B7=E6=B1=82=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=9C=89=E5=9B=BA=E5=AE=9A=E5=9B=9E=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gtp/gtp.go | 28 +++++++++++++++++----------- handlers/group_msg_handler.go | 8 +++++++- handlers/user_msg_handler.go | 1 + 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/gtp/gtp.go b/gtp/gtp.go index 15cd8e2..047c268 100644 --- a/gtp/gtp.go +++ b/gtp/gtp.go @@ -3,6 +3,8 @@ package gtp import ( "bytes" "encoding/json" + "errors" + "fmt" "github.com/869413421/wechatbot/config" "io/ioutil" "log" @@ -13,15 +15,19 @@ const BASEURL = "https://api.openai.com/v1/" // ChatGPTResponseBody 请求体 type ChatGPTResponseBody struct { - ID string `json:"id"` - Object string `json:"object"` - Created int `json:"created"` - Model string `json:"model"` - Choices []map[string]interface{} `json:"choices"` - Usage map[string]interface{} `json:"usage"` + ID string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + Model string `json:"model"` + Choices []ChoiceItem `json:"choices"` + Usage map[string]interface{} `json:"usage"` } type ChoiceItem struct { + Text string `json:"text"` + Index int `json:"index"` + Logprobs int `json:"logprobs"` + FinishReason string `json:"finish_reason"` } // ChatGPTRequestBody 响应体 @@ -70,7 +76,9 @@ func Completions(msg string) (string, error) { return "", err } defer response.Body.Close() - + if response.StatusCode != 200 { + return "", errors.New(fmt.Sprintf("gtp api status code not equals 200,code is %d", response.StatusCode)) + } body, err := ioutil.ReadAll(response.Body) if err != nil { return "", err @@ -82,12 +90,10 @@ func Completions(msg string) (string, error) { if err != nil { return "", err } + var reply string if len(gptResponseBody.Choices) > 0 { - for _, v := range gptResponseBody.Choices { - reply = v["text"].(string) - break - } + reply = gptResponseBody.Choices[0].Text } log.Printf("gpt response text: %s \n", reply) return reply, nil diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index ec0560a..3fd27a0 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -41,10 +41,16 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { // 替换掉@文本,然后向GPT发起请求 replaceText := "@" + sender.Self.NickName requestText := strings.TrimSpace(strings.ReplaceAll(msg.Content, replaceText, "")) + if requestText == "" { + return nil + } reply, err := gtp.Completions(requestText) if err != nil { log.Printf("gtp request error: %v \n", err) - msg.ReplyText("机器人神了,我一会发现了就去修。") + _, err = msg.ReplyText("机器人神了,我一会发现了就去修。") + if err != nil { + log.Printf("response group error: %v \n", err) + } return err } if reply == "" { diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index e2e250f..9861a9c 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -48,6 +48,7 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { // 回复用户 reply = strings.TrimSpace(reply) reply = strings.Trim(reply, "\n") + reply = "本消息由 chatGPT Bot回复:\n" + reply _, err = msg.ReplyText(reply) if err != nil { log.Printf("response user error: %v \n", err) From e3a8ac62bf112da3f6b862231e6c1c8c917d7c06 Mon Sep 17 00:00:00 2001 From: huangyanming Date: Thu, 8 Dec 2022 08:18:06 +0800 Subject: [PATCH 02/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cc69d9b..5de584c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # wechatbot 最近chatGPT异常火爆,想到将其接入到个人微信是件比较有趣的事,所以有了这个项目。项目基于[openwechat](https://github.com/eatmoreapple/openwechat) 开发 -###目前实现了以下功能 - + 群聊@回复 - + 私聊回复 - + 自动通过回复 +### 目前实现了以下功能 + + 机器人群聊@回复 + + 机器人私聊回复 + + 好友添加自动通过 # 注册openai chatGPT注册可以参考[这里](https://juejin.cn/post/7173447848292253704) +,注册完在控制台右上角点用户头像 ViewApiKeys中新增加一个api_key # 安装使用 ```` @@ -24,3 +25,4 @@ copy config.dev.json config.json go run main.go 启动前需替换config中的api_key +```` \ No newline at end of file From 9b979d8090dbc718f9c5a3927144b2a85351b087 Mon Sep 17 00:00:00 2001 From: huangyanming Date: Thu, 8 Dec 2022 11:33:25 +0800 Subject: [PATCH 03/37] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8F=90=E9=97=AE?= =?UTF-8?q?=E4=B8=8A=E4=B8=8B=E9=97=AE=EF=BC=8C=E6=8E=A5=E8=BF=91=E5=AE=98?= =?UTF-8?q?=E7=BD=91=E6=95=88=E6=9E=9C=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++--- go.mod | 5 ++++- go.sum | 2 ++ gtp/gtp.go | 2 +- handlers/group_msg_handler.go | 5 ++++- handlers/handler.go | 4 ++++ handlers/user_msg_handler.go | 7 ++++-- service/user.go | 40 +++++++++++++++++++++++++++++++++++ 8 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 service/user.go diff --git a/README.md b/README.md index 5de584c..6e35c35 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ 最近chatGPT异常火爆,想到将其接入到个人微信是件比较有趣的事,所以有了这个项目。项目基于[openwechat](https://github.com/eatmoreapple/openwechat) 开发 ### 目前实现了以下功能 - + 机器人群聊@回复 - + 机器人私聊回复 - + 好友添加自动通过 + * 提问增加上下文,更接近官网效果 + * 机器人群聊@回复 + * 机器人私聊回复 + * 好友添加自动通过 # 注册openai chatGPT注册可以参考[这里](https://juejin.cn/post/7173447848292253704) diff --git a/go.mod b/go.mod index a3f92ad..edc8d1a 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module github.com/869413421/wechatbot go 1.16 -require github.com/eatmoreapple/openwechat v1.2.1 +require ( + github.com/eatmoreapple/openwechat v1.2.1 + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect +) diff --git a/go.sum b/go.sum index 6fe8c3d..d805d83 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ github.com/eatmoreapple/openwechat v1.2.1 h1:ez4oqF/Y2NSEX/DbPV8lvj7JlfkYqvieeo4awx5lzfU= github.com/eatmoreapple/openwechat v1.2.1/go.mod h1:61HOzTyvLobGdgWhL68jfGNwTJEv0mhQ1miCXQrvWU8= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= diff --git a/gtp/gtp.go b/gtp/gtp.go index 047c268..471fab3 100644 --- a/gtp/gtp.go +++ b/gtp/gtp.go @@ -50,7 +50,7 @@ func Completions(msg string) (string, error) { requestBody := ChatGPTRequestBody{ Model: "text-davinci-003", Prompt: msg, - MaxTokens: 2048, + MaxTokens: 1024, Temperature: 0.7, TopP: 1, FrequencyPenalty: 0, diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 3fd27a0..7ec3e6a 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -38,12 +38,13 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { return nil } - // 替换掉@文本,然后向GPT发起请求 + // 替换掉@文本,设置会话上下文,然后向GPT发起请求。 replaceText := "@" + sender.Self.NickName requestText := strings.TrimSpace(strings.ReplaceAll(msg.Content, replaceText, "")) if requestText == "" { return nil } + requestText = UserService.GetUserSessionContext(sender.ID()) + requestText reply, err := gtp.Completions(requestText) if err != nil { log.Printf("gtp request error: %v \n", err) @@ -67,6 +68,8 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { // 回复@我的用户 reply = strings.TrimSpace(reply) reply = strings.Trim(reply, "\n") + // 设置上下文 + UserService.SetUserSessionContext(sender.ID(), requestText, reply) atText := "@" + groupSender.NickName replyText := atText + reply _, err = msg.ReplyText(replyText) diff --git a/handlers/handler.go b/handlers/handler.go index 50d2b54..882d6c8 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -2,6 +2,7 @@ package handlers import ( "github.com/869413421/wechatbot/config" + "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" "log" ) @@ -21,11 +22,14 @@ const ( // handlers 所有消息类型类型的处理器 var handlers map[HandlerType]MessageHandlerInterface +var UserService service.UserServiceInterface func init() { handlers = make(map[HandlerType]MessageHandlerInterface) handlers[GroupHandler] = NewGroupMessageHandler() handlers[UserHandler] = NewUserMessageHandler() + + UserService = service.NewUserService() } // Handler 全局处理入口 diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 9861a9c..73fbd34 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -32,9 +32,11 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { sender, err := msg.Sender() log.Printf("Received User %v Text Msg : %v", sender.NickName, msg.Content) - // 向GPT发起请求 + // 获取上下文,向GPT发起请求 requestText := strings.TrimSpace(msg.Content) requestText = strings.Trim(msg.Content, "\n") + + requestText = UserService.GetUserSessionContext(sender.ID()) + requestText reply, err := gtp.Completions(requestText) if err != nil { log.Printf("gtp request error: %v \n", err) @@ -45,9 +47,10 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { return nil } - // 回复用户 + // 设置上下文,回复用户 reply = strings.TrimSpace(reply) reply = strings.Trim(reply, "\n") + UserService.SetUserSessionContext(sender.ID(), requestText, reply) reply = "本消息由 chatGPT Bot回复:\n" + reply _, err = msg.ReplyText(reply) if err != nil { diff --git a/service/user.go b/service/user.go new file mode 100644 index 0000000..e4ebbbb --- /dev/null +++ b/service/user.go @@ -0,0 +1,40 @@ +package service + +import ( + "github.com/patrickmn/go-cache" + "time" +) + +// UserServiceInterface 用户业务接口 +type UserServiceInterface interface { + GetUserSessionContext(userId string) string + SetUserSessionContext(userId string, question, reply string) +} + +var _ UserServiceInterface = (*UserService)(nil) + +// UserService 用戶业务 +type UserService struct { + // 缓存 + cache *cache.Cache +} + +// NewUserService 创建新的业务层 +func NewUserService() UserServiceInterface { + return &UserService{cache: cache.New(time.Minute*5, time.Minute*10)} +} + +// GetUserSessionContext 获取用户会话上下文文本 +func (s *UserService) GetUserSessionContext(userId string) string { + sessionContext, ok := s.cache.Get(userId) + if !ok { + return "" + } + return sessionContext.(string) +} + +// SetUserSessionContext 设置用户会话上下文文本,question用户提问内容,GTP回复内容 +func (s *UserService) SetUserSessionContext(userId string, question, reply string) { + value := question + "\n" + reply + s.cache.Set(userId, value, time.Minute*5) +} From ba9003ecf86014325c52617502bc108eacdbba4a Mon Sep 17 00:00:00 2001 From: huangyanming Date: Thu, 8 Dec 2022 12:16:49 +0800 Subject: [PATCH 04/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + README.md | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index caccd51..f1d23ba 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ storage.json # Dependency directories (remove the comment below to include it) # vendor/ +/config.json diff --git a/README.md b/README.md index 6e35c35..79af7d0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # wechatbot -最近chatGPT异常火爆,想到将其接入到个人微信是件比较有趣的事,所以有了这个项目。项目基于[openwechat](https://github.com/eatmoreapple/openwechat) -开发 +最近chatGPT异常火爆,想到将其接入到个人微信是件比较有趣的事,所以有了这个项目,项目基于[openwechat](https://github.com/eatmoreapple/openwechat) +开发。 +![Release](https://github.com/869413421/wechatbot/releases/tag/v1.0.1) ### 目前实现了以下功能 * 提问增加上下文,更接近官网效果 * 机器人群聊@回复 @@ -9,7 +10,7 @@ # 注册openai chatGPT注册可以参考[这里](https://juejin.cn/post/7173447848292253704) -,注册完在控制台右上角点用户头像 ViewApiKeys中新增加一个api_key +,注册完在控制台右上角点用户头像ViewApiKeys中新增加一个api_key。 # 安装使用 ```` From f80a2118de201017a5ab11e7bdaa578822eff24f Mon Sep 17 00:00:00 2001 From: huangyanming Date: Thu, 8 Dec 2022 16:09:00 +0800 Subject: [PATCH 05/37] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E6=97=B6=E9=95=BF=E9=85=8D=E7=BD=AE=E5=8A=9F=E8=83=BD=202.?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=BB=E5=8A=A8=E6=B8=85=E7=A9=BA=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 52 +++++++++++++++++++++++++++++------ config.dev.json | 3 +- config/config.go | 16 ++++++++++- handlers/group_msg_handler.go | 41 +++++++++++++++++++-------- handlers/user_msg_handler.go | 7 +++++ service/user.go | 17 ++++++++++-- 6 files changed, 111 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 79af7d0..595a84f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,26 @@ # wechatbot -最近chatGPT异常火爆,想到将其接入到个人微信是件比较有趣的事,所以有了这个项目,项目基于[openwechat](https://github.com/eatmoreapple/openwechat) -开发。 -![Release](https://github.com/869413421/wechatbot/releases/tag/v1.0.1) +> 最近chatGPT异常火爆,本项目可以将个人微信化身GPT机器人, +> 项目基于[openwechat](https://github.com/eatmoreapple/openwechat) 开发。 + +[![Release](https://img.shields.io/github/v/release/869413421/wechatbot.svg?style=flat-square)](https://github.com/869413421/wechatbot/releases/tag/v1.0.1) +![Github stars](https://img.shields.io/github/stars/869413421/wechatbot.svg) +![Forks](https://img.shields.io/github/forks/869413421/wechatbot.svg?style=flat-square) + ### 目前实现了以下功能 * 提问增加上下文,更接近官网效果 * 机器人群聊@回复 * 机器人私聊回复 * 好友添加自动通过 -# 注册openai -chatGPT注册可以参考[这里](https://juejin.cn/post/7173447848292253704) -,注册完在控制台右上角点用户头像ViewApiKeys中新增加一个api_key。 +# 使用前提 +> * 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。 +> * 微信必须实名认证。 -# 安装使用 +# 注意事项 +> * 项目仅供娱乐,滥用可能有微信封禁的风险,请勿用于商业用途。 +> * 请注意收发敏感信息,本项目不做信息过滤。 + +# 快速开始 ```` # 获取项目 git clone https://github.com/869413421/wechatbot.git @@ -25,6 +33,32 @@ copy config.dev.json config.json # 启动项目 go run main.go +```` + +# 配置文件说明 +```` +{ +"api_key": "your api key", +"auto_pass": true, +"session_timeout": 60 +} + +api_key:openai api_key +auto_pass:是否自动通过好友添加 +session_timeout:会话超时时间,默认60秒,单位秒,在会话时间内所有发送给机器人的信息会作为上下文。 +```` + +# 使用示例 +### 向机器人佛送`我要问下一个问题`,清空会话信息。 +### 私聊 + + +### 群聊@回复 + + +### 添加微信(备注: wechabot)进群交流 + +**如果二维码图片没显示出来,请添加微信号 huangyanming681925** + + -启动前需替换config中的api_key -```` \ No newline at end of file diff --git a/config.dev.json b/config.dev.json index b78e4db..73d2ef2 100644 --- a/config.dev.json +++ b/config.dev.json @@ -1,4 +1,5 @@ { "api_key": "your api key", - "auto_pass": true + "auto_pass": true, + "session_timeout": 60 } diff --git a/config/config.go b/config/config.go index 6afede1..5764ff0 100644 --- a/config/config.go +++ b/config/config.go @@ -5,6 +5,7 @@ import ( "log" "os" "sync" + "time" ) // Configuration 项目配置 @@ -13,6 +14,8 @@ type Configuration struct { ApiKey string `json:"api_key"` // 自动通过好友 AutoPass bool `json:"auto_pass"` + // 会话超时时间 + SessionTimeout time.Duration `json:"session_timeout"` } var config *Configuration @@ -22,7 +25,9 @@ var once sync.Once func LoadConfig() *Configuration { once.Do(func() { // 从文件中读取 - config = &Configuration{} + config = &Configuration{ + SessionTimeout: 1, + } f, err := os.Open("config.json") if err != nil { log.Fatalf("open config err: %v", err) @@ -39,12 +44,21 @@ func LoadConfig() *Configuration { // 如果环境变量有配置,读取环境变量 ApiKey := os.Getenv("ApiKey") AutoPass := os.Getenv("AutoPass") + SessionTimeout := os.Getenv("SessionTimeout") if ApiKey != "" { config.ApiKey = ApiKey } if AutoPass == "true" { config.AutoPass = true } + if SessionTimeout != "" { + duration, err := time.ParseDuration(SessionTimeout) + if err != nil { + log.Fatalf("config decode session timeout err: %v ,get is %v", err, SessionTimeout) + return + } + config.SessionTimeout = duration + } }) return config } diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 7ec3e6a..a484bc7 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -30,7 +30,7 @@ func NewGroupMessageHandler() MessageHandlerInterface { func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { // 接收群消息 sender, err := msg.Sender() - group := openwechat.Group{sender} + group := openwechat.Group{User: sender} log.Printf("Received Group %v Text Msg : %v", group.NickName, msg.Content) // 不是@的不处理 @@ -38,13 +38,27 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { return nil } + // 获取@我的用户 + groupSender, err := msg.SenderInGroup() + if err != nil { + log.Printf("get sender in group error :%v \n", err) + return err + } + atText := "@" + groupSender.NickName + " " + + if UserService.ClearUserSessionContext(sender.ID(), msg.Content) { + _, err = msg.ReplyText(atText + "上下文已经清空了,你可以问下一个问题啦。") + if err != nil { + log.Printf("response user error: %v \n", err) + } + return nil + } + // 替换掉@文本,设置会话上下文,然后向GPT发起请求。 - replaceText := "@" + sender.Self.NickName - requestText := strings.TrimSpace(strings.ReplaceAll(msg.Content, replaceText, "")) + requestText := buildRequestText(sender, msg) if requestText == "" { return nil } - requestText = UserService.GetUserSessionContext(sender.ID()) + requestText reply, err := gtp.Completions(requestText) if err != nil { log.Printf("gtp request error: %v \n", err) @@ -58,19 +72,11 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { return nil } - // 获取@我的用户 - groupSender, err := msg.SenderInGroup() - if err != nil { - log.Printf("get sender in group error :%v \n", err) - return err - } - // 回复@我的用户 reply = strings.TrimSpace(reply) reply = strings.Trim(reply, "\n") // 设置上下文 UserService.SetUserSessionContext(sender.ID(), requestText, reply) - atText := "@" + groupSender.NickName replyText := atText + reply _, err = msg.ReplyText(replyText) if err != nil { @@ -78,3 +84,14 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { } return err } + +// buildRequestText 构建请求GPT的文本,替换掉机器人名称,然后检查是否有上下文,如果有拼接上 +func buildRequestText(sender *openwechat.User, msg *openwechat.Message) string { + replaceText := "@" + sender.Self.NickName + requestText := strings.TrimSpace(strings.ReplaceAll(msg.Content, replaceText, "")) + if requestText == "" { + return "" + } + requestText = UserService.GetUserSessionContext(sender.ID()) + requestText + return requestText +} diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 73fbd34..5fbd38d 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -31,6 +31,13 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { // 接收私聊消息 sender, err := msg.Sender() log.Printf("Received User %v Text Msg : %v", sender.NickName, msg.Content) + if UserService.ClearUserSessionContext(sender.ID(), msg.Content) { + _, err = msg.ReplyText("上下文已经清空了,你可以问下一个问题啦。") + if err != nil { + log.Printf("response user error: %v \n", err) + } + return nil + } // 获取上下文,向GPT发起请求 requestText := strings.TrimSpace(msg.Content) diff --git a/service/user.go b/service/user.go index e4ebbbb..89e1ef9 100644 --- a/service/user.go +++ b/service/user.go @@ -1,14 +1,18 @@ package service import ( + "github.com/869413421/wechatbot/config" "github.com/patrickmn/go-cache" + "strings" "time" + "unicode/utf8" ) // UserServiceInterface 用户业务接口 type UserServiceInterface interface { GetUserSessionContext(userId string) string SetUserSessionContext(userId string, question, reply string) + ClearUserSessionContext(userId string, msg string) bool } var _ UserServiceInterface = (*UserService)(nil) @@ -19,9 +23,18 @@ type UserService struct { cache *cache.Cache } +// ClearUserSessionContext 清空GTP上下文,接收文本中包含`我要问下一个问题`,并且Unicode 字符数量不超过20就清空 +func (s *UserService) ClearUserSessionContext(userId string, msg string) bool { + if strings.Contains(msg, "我要问下一个问题") && utf8.RuneCountInString(msg) < 20 { + s.cache.Delete(userId) + return true + } + return false +} + // NewUserService 创建新的业务层 func NewUserService() UserServiceInterface { - return &UserService{cache: cache.New(time.Minute*5, time.Minute*10)} + return &UserService{cache: cache.New(time.Second*config.LoadConfig().SessionTimeout, time.Minute*10)} } // GetUserSessionContext 获取用户会话上下文文本 @@ -36,5 +49,5 @@ func (s *UserService) GetUserSessionContext(userId string) string { // SetUserSessionContext 设置用户会话上下文文本,question用户提问内容,GTP回复内容 func (s *UserService) SetUserSessionContext(userId string, question, reply string) { value := question + "\n" + reply - s.cache.Set(userId, value, time.Minute*5) + s.cache.Set(userId, value, time.Second*config.LoadConfig().SessionTimeout) } From 6cd1ccc4c16e8e51c437d259f66cbb9a871c2e7c Mon Sep 17 00:00:00 2001 From: huangyanming Date: Thu, 8 Dec 2022 17:39:59 +0800 Subject: [PATCH 06/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 595a84f..c58470e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ * 好友添加自动通过 # 使用前提 +> * 目前只支持在windows上运行因为需要弹窗扫码登录微信,后续会支持linux > * 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。 > * 微信必须实名认证。 @@ -21,6 +22,7 @@ > * 请注意收发敏感信息,本项目不做信息过滤。 # 快速开始 +> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.1) ,解压运行。 ```` # 获取项目 git clone https://github.com/869413421/wechatbot.git From ef2fa38f1ea97b6dc3f005b175960d778bbc9972 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Thu, 8 Dec 2022 21:21:05 +0800 Subject: [PATCH 07/37] =?UTF-8?q?=E6=94=AF=E6=8C=81linux=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bootstrap/bootstrap.go | 5 +++-- go.mod | 3 ++- go.sum | 2 ++ handlers/handler.go | 16 +++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 7049740..924ace9 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -7,18 +7,19 @@ import ( ) - func Run() { //bot := openwechat.DefaultBot() bot := openwechat.DefaultBot(openwechat.Desktop) // 桌面模式,上面登录不上的可以尝试切换这种模式 // 注册消息处理函数 bot.MessageHandler = handlers.Handler + // 注册登陆二维码回调 - bot.UUIDCallback = openwechat.PrintlnQrcodeUrl + bot.UUIDCallback = handlers.QrCodeCallBack // 创建热存储容器对象 reloadStorage := openwechat.NewJsonFileHotReloadStorage("storage.json") + // 执行热登录 err := bot.HotLogin(reloadStorage) if err != nil { diff --git a/go.mod b/go.mod index edc8d1a..3d6f011 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,6 @@ go 1.16 require ( github.com/eatmoreapple/openwechat v1.2.1 - github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e ) diff --git a/go.sum b/go.sum index d805d83..19f6633 100644 --- a/go.sum +++ b/go.sum @@ -2,3 +2,5 @@ github.com/eatmoreapple/openwechat v1.2.1 h1:ez4oqF/Y2NSEX/DbPV8lvj7JlfkYqvieeo4 github.com/eatmoreapple/openwechat v1.2.1/go.mod h1:61HOzTyvLobGdgWhL68jfGNwTJEv0mhQ1miCXQrvWU8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= diff --git a/handlers/handler.go b/handlers/handler.go index 882d6c8..f1bf4fb 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -1,10 +1,13 @@ package handlers import ( + "fmt" "github.com/869413421/wechatbot/config" "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" + "github.com/skip2/go-qrcode" "log" + "runtime" ) // MessageHandlerInterface 消息处理接口 @@ -20,6 +23,18 @@ const ( UserHandler = "user" ) +// QrCodeCallBack 登录扫码回调, +func QrCodeCallBack(uuid string) { + if runtime.GOOS == "windows" { + // 运行在Windows系统上 + openwechat.PrintlnQrcodeUrl(uuid) + } else { + log.Println("login in linux") + q, _ := qrcode.New("https://login.weixin.qq.com/l/"+uuid, qrcode.Low) + fmt.Println(q.ToString(true)) + } +} + // handlers 所有消息类型类型的处理器 var handlers map[HandlerType]MessageHandlerInterface var UserService service.UserServiceInterface @@ -28,7 +43,6 @@ func init() { handlers = make(map[HandlerType]MessageHandlerInterface) handlers[GroupHandler] = NewGroupMessageHandler() handlers[UserHandler] = NewUserMessageHandler() - UserService = service.NewUserService() } From 1b22bee750ac40aabcd82073b8aa7477f5f63b58 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Thu, 8 Dec 2022 21:22:53 +0800 Subject: [PATCH 08/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c58470e..a28873e 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ * 好友添加自动通过 # 使用前提 -> * 目前只支持在windows上运行因为需要弹窗扫码登录微信,后续会支持linux +> * ~~目前只支持在windows上运行因为需要弹窗扫码登录微信,后续会支持linux~~ 已支持 > * 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。 > * 微信必须实名认证。 @@ -51,7 +51,7 @@ session_timeout:会话超时时间,默认60秒,单位秒,在会话时间 ```` # 使用示例 -### 向机器人佛送`我要问下一个问题`,清空会话信息。 +### 向机器人发送`我要问下一个问题`,清空会话信息。 ### 私聊 From 529905df9ac9ccd5b3be4a673fde9d1a3ad8263a Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Thu, 8 Dec 2022 23:27:43 +0800 Subject: [PATCH 09/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 ++++++ handlers/user_msg_handler.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a28873e..f3abeb6 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,12 @@ copy config.dev.json config.json # 启动项目 go run main.go + +# linux编译,守护进程运行(可选) +# 编译 +CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o wechatbot ./main.go +# 守护进程运行 +nohup ./wechatbot > run.log & ```` # 配置文件说明 diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 5fbd38d..3372e6f 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -58,7 +58,7 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { reply = strings.TrimSpace(reply) reply = strings.Trim(reply, "\n") UserService.SetUserSessionContext(sender.ID(), requestText, reply) - reply = "本消息由 chatGPT Bot回复:\n" + reply + reply = "本消息由ChatGPTBot回复:\n" + reply _, err = msg.ReplyText(reply) if err != nil { log.Printf("response user error: %v \n", err) From e8298bbd912bffff13c86d01684ff75edacd5b57 Mon Sep 17 00:00:00 2001 From: eryajf Date: Fri, 9 Dec 2022 09:14:20 +0800 Subject: [PATCH 10/37] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E9=A1=B9=E7=9B=AE=E9=95=9C=E5=83=8F=E7=9A=84?= =?UTF-8?q?=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 61 ++++++++++++++++++++++++++++++ .gitignore | 2 + Dockerfile | 16 ++++++++ 3 files changed, 79 insertions(+) create mode 100644 .github/workflows/docker-image.yml create mode 100644 Dockerfile diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..0985e0d --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,61 @@ +# This is a basic workflow to help you get started with Actions + +name: build docker image + +# Controls when the action will run. +on: + push: + branches: + - main + +# Allows you to run this workflow manually from the Actions tab + # 可以手动触发 + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + tags: + description: 'Test scenario tags' + +jobs: + buildx: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Get current date + id: date + run: echo "::set-output name=today::$(date +'%Y-%m-%d_%H-%M')" + + - name: Set up QEMU + uses: docker/setup-qemu-action@v1 + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Available platforms + run: echo ${{ steps.buildx.outputs.platforms }} + + - name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + # 所需要的体系结构,可以在 Available platforms 步骤中获取所有的可用架构 + platforms: linux/amd64,linux/arm64/v8 + # 镜像推送时间 + push: ${{ github.event_name != 'pull_request' }} + # 给清单打上多个标签 + tags: | + eryajf/wechatbot:${{ steps.date.outputs.today }} + eryajf/wechatbot:latest \ No newline at end of file diff --git a/.gitignore b/.gitignore index f1d23ba..f7dea7e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ *.so *.dylib .idea/ +.vscode/ +wechatbot storage.json # Test binary, built with `go test -c` diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..13e878d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM golang:1.17.10 AS builder + +# ENV GOPROXY https://goproxy.io + +RUN mkdir /app +ADD . /app/ +WORKDIR /app +RUN go build -o wechatbot . + +FROM centos:centos7 +RUN mkdir /app +WORKDIR /app +COPY --from=builder /app/ . +RUN chmod +x wechatbot && cp config.dev.json config.json && yum -y install vim net-tools telnet wget curl && yum clean all + +CMD ./wechatbot \ No newline at end of file From c761ab4b31ac0a917498aa3bfdd4f1c04acb14c8 Mon Sep 17 00:00:00 2001 From: eryajf Date: Fri, 9 Dec 2022 09:45:11 +0800 Subject: [PATCH 11/37] =?UTF-8?q?=E6=B7=BB=E5=8A=A0readme=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 4 ++-- README.md | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 0985e0d..b2b1091 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -57,5 +57,5 @@ jobs: push: ${{ github.event_name != 'pull_request' }} # 给清单打上多个标签 tags: | - eryajf/wechatbot:${{ steps.date.outputs.today }} - eryajf/wechatbot:latest \ No newline at end of file + ${{ secrets.DOCKERHUB_USERNAME }}/wechatbot:${{ steps.date.outputs.today }} + ${{ secrets.DOCKERHUB_USERNAME }}/wechatbot:latest \ No newline at end of file diff --git a/README.md b/README.md index f3abeb6..a7f00c6 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ ![Forks](https://img.shields.io/github/forks/869413421/wechatbot.svg?style=flat-square) ### 目前实现了以下功能 - * 提问增加上下文,更接近官网效果 + * 提问增加上下文,更接近官网效果 * 机器人群聊@回复 * 机器人私聊回复 * 好友添加自动通过 - + # 使用前提 > * ~~目前只支持在windows上运行因为需要弹窗扫码登录微信,后续会支持linux~~ 已支持 > * 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。 @@ -21,6 +21,30 @@ > * 项目仅供娱乐,滥用可能有微信封禁的风险,请勿用于商业用途。 > * 请注意收发敏感信息,本项目不做信息过滤。 +# 使用docker运行 + +你可以使用docker快速运行本项目。 + +`第一种:基于环境变量运行` + +```sh +docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTimeout=60 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +``` + +其中配置文件参考下边的配置文件说明。 + +`第二种:基于配置文件挂载运行` + +```sh +# 复制配置文件,根据自己实际情况,调整配置里的内容 +cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录获取 + +# 运行项目 +docker run -itd --name wechatbot -v ./config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +``` + +其中配置文件参考下边的配置文件说明。 + # 快速开始 > 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.1) ,解压运行。 ```` From 04b51e789f4e4e0772ec2a461d08d85ccdbccc4e Mon Sep 17 00:00:00 2001 From: eryajf Date: Fri, 9 Dec 2022 09:51:58 +0800 Subject: [PATCH 12/37] =?UTF-8?q?readme=E6=B7=BB=E5=8A=A0docker=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E5=8F=97=E5=91=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7f00c6..46721f2 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,11 @@ `第一种:基于环境变量运行` ```sh -docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTimeout=60 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +# 运行项目 +$ docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTimeout=60 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest + +# 查看二维码 +$ docker logs -f wechatbot ``` 其中配置文件参考下边的配置文件说明。 @@ -41,6 +45,9 @@ cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录 # 运行项目 docker run -itd --name wechatbot -v ./config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest + +# 查看二维码 +$ docker logs -f wechatbot ``` 其中配置文件参考下边的配置文件说明。 From db61c4d523576567f6983d5429ed8c964421f6a9 Mon Sep 17 00:00:00 2001 From: eryajf Date: Fri, 9 Dec 2022 09:52:39 +0800 Subject: [PATCH 13/37] =?UTF-8?q?readme=E6=B7=BB=E5=8A=A0docker=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 46721f2..80c3db2 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ $ docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTi $ docker logs -f wechatbot ``` -其中配置文件参考下边的配置文件说明。 +运行命令中映射的配置文件参考下边的配置文件说明。 `第二种:基于配置文件挂载运行` From e3dba40857cf52b920cd25b8eada59282177c39b Mon Sep 17 00:00:00 2001 From: Roger <34738478+adsian@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:00:15 +0800 Subject: [PATCH 14/37] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SessionTimeout=60s 需要加 s --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80c3db2..0bc5cf2 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ ```sh # 运行项目 -$ docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTimeout=60 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +$ docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTimeout=60s docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 $ docker logs -f wechatbot From 652b7cecd2f78767092e20f45122756d02001f43 Mon Sep 17 00:00:00 2001 From: huangyanming Date: Fri, 9 Dec 2022 17:58:40 +0800 Subject: [PATCH 15/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/config.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 5764ff0..0c3dabc 100644 --- a/config/config.go +++ b/config/config.go @@ -16,6 +16,12 @@ type Configuration struct { AutoPass bool `json:"auto_pass"` // 会话超时时间 SessionTimeout time.Duration `json:"session_timeout"` + // GPT请求最大字符数 + MaxTokens uint `json:"max_tokens"` + // GPT模型 + Model string `json:"model"` + // 热度 + Temperature float32 `json:"temperature"` } var config *Configuration @@ -26,7 +32,7 @@ func LoadConfig() *Configuration { once.Do(func() { // 从文件中读取 config = &Configuration{ - SessionTimeout: 1, + SessionTimeout: 60, } f, err := os.Open("config.json") if err != nil { From c5b53041aa84208d7ab3c757847575f3f368666d Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Fri, 9 Dec 2022 23:37:57 +0800 Subject: [PATCH 16/37] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0GTP=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=202.Dockerfile=E4=BC=98=E5=8C=96=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0supervisord=EF=BC=8C=E8=BF=9B=E7=A8=8B?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E8=87=AA=E5=8A=A8=E6=8B=89=E8=B5=B7=203.?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=A4=84=E7=90=86=E5=99=A8=E6=8D=95=E8=8E=B7?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=EF=BC=8C=E9=81=BF=E5=85=8D=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E5=B4=A9=E6=BA=83=204.=E4=BC=98=E5=8C=96=E4=BA=8C=E4=BD=8D?= =?UTF-8?q?=E7=A0=81=E5=B0=BA=E5=AF=B8=EF=BC=8Clinux=E4=B8=8A=E6=9B=B4?= =?UTF-8?q?=E5=AE=B9=E6=98=93=E6=89=AB=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 67 ++++++++++++++++++++++++++++++++------ Makefile | 7 ++++ README.md | 23 +++++++++---- config.dev.json | 7 ++-- config/config.go | 78 +++++++++++++++++++++++++++++++++------------ gtp/gtp.go | 13 +++++--- handlers/handler.go | 12 +++++-- supervisord.conf | 13 ++++++++ 8 files changed, 175 insertions(+), 45 deletions(-) create mode 100644 Makefile create mode 100644 supervisord.conf diff --git a/Dockerfile b/Dockerfile index 13e878d..6523b78 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,65 @@ -FROM golang:1.17.10 AS builder +#FROM golang:1.17.10 AS builder +# +## ENV GOPROXY https://goproxy.io +# +#RUN mkdir /app +#ADD . /app/ +#WORKDIR /app +#RUN go build -o wechatbot . +# +#FROM centos:centos7 +#RUN mkdir /app +#WORKDIR /app +#COPY --from=builder /app/ . +#RUN chmod +x wechatbot && cp config.dev.json config.json && yum -y install vim net-tools telnet wget curl && yum clean all +# +#CMD ./wechatbot -# ENV GOPROXY https://goproxy.io +# wechatbot/Dockerfile -RUN mkdir /app -ADD . /app/ +# 使用 golang 官方镜像提供 Go 运行环境,并且命名为 buidler 以便后续引用 +FROM golang:1.16-alpine as builder + +# 启用 Go Modules 并设置 GOPROXY +ENV GO111MODULE on +ENV GOPROXY https://goproxy.cn + +# 更新安装源 +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +# 安装 git +RUN apk --no-cache add git + +# 设置工作目录 WORKDIR /app -RUN go build -o wechatbot . -FROM centos:centos7 +# 将当前项目所在目录代码拷贝到镜像中 +COPY . . + +# 下载依赖 +RUN go mod download + +# 构建二进制文件,添加来一些额外参数以便可以在 Alpine 中运行它 +RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o wechatbot + +# 下面是第二阶段的镜像构建,和之前保持一致 +FROM alpine:latest + +# 更新安装源 +RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories + +# 安装相关软件 +RUN apk update && apk add --no-cache bash supervisor ca-certificates + +# 和上个阶段一样设置工作目录 RUN mkdir /app WORKDIR /app -COPY --from=builder /app/ . -RUN chmod +x wechatbot && cp config.dev.json config.json && yum -y install vim net-tools telnet wget curl && yum clean all -CMD ./wechatbot \ No newline at end of file +# 而是从上一个阶段构建的 builder容器中拉取 +COPY --from=builder /app/wechatbot . +ADD supervisord.conf /etc/supervisord.conf +ADD config.dev.json /app/config.dev.json +RUN cp config.dev.json config.json + +# 通过 Supervisor 管理服务 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a1a8300 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +.PHONY: build +build: + CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o wechatbot ./main.go + +.PHONY: docker +docker: + docker build . -t wechatbot:latest diff --git a/README.md b/README.md index 0bc5cf2..2899fb1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ ![Forks](https://img.shields.io/github/forks/869413421/wechatbot.svg?style=flat-square) ### 目前实现了以下功能 + * GPT机器人模型热度可配置 * 提问增加上下文,更接近官网效果 * 机器人群聊@回复 * 机器人私聊回复 @@ -28,11 +29,12 @@ `第一种:基于环境变量运行` ```sh -# 运行项目 -$ docker run -itd --name wechatbot -e ApiKey=xxxx -e AutoPass=false -e SessionTimeout=60s docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +# 运行项目,环境变量参考下方配置说明 +$ docker run -itd --name wechatbot --restart=always -e APIKEY=xxxx -e AUTO_PASS=false -e SESSION_TIMEOUT=60s -e MODEL=text-davinci-003 -e MAX_TOKENS=512 -e TEMPREATURE=0.9 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 -$ docker logs -f wechatbot +$ docker exec -it wechatbot bash +$ tail -f -n 50 /app/run.log ``` 运行命令中映射的配置文件参考下边的配置文件说明。 @@ -47,7 +49,8 @@ cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录 docker run -itd --name wechatbot -v ./config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 -$ docker logs -f wechatbot +$ docker exec -it wechatbot bash +$ tail -f -n 50 /app/run.log ``` 其中配置文件参考下边的配置文件说明。 @@ -77,14 +80,20 @@ nohup ./wechatbot > run.log & # 配置文件说明 ```` { -"api_key": "your api key", -"auto_pass": true, -"session_timeout": 60 + "api_key": "your api key", + "auto_pass": false, + "session_timeout": 60, + "max_tokens": 512, + "model": "text-davinci-003", + "temperature": 0.9 } api_key:openai api_key auto_pass:是否自动通过好友添加 session_timeout:会话超时时间,默认60秒,单位秒,在会话时间内所有发送给机器人的信息会作为上下文。 +max_tokens: GPT响应字符数,最大2048,默认值512。max_tokens会影响接口响应速度,字符越大响应越慢。 +model: GTP选用模型,默认text-davinci-003,具体选项参考官网训练场 +temperature: GTP热度,0到1,默认0.9。数字越大创造力越强,但更偏离训练事实,越低越接近训练事实 ```` # 使用示例 diff --git a/config.dev.json b/config.dev.json index 73d2ef2..e66f3ec 100644 --- a/config.dev.json +++ b/config.dev.json @@ -1,5 +1,8 @@ { "api_key": "your api key", - "auto_pass": true, - "session_timeout": 60 + "auto_pass": false, + "session_timeout": 60, + "max_tokens": 512, + "model": "text-davinci-003", + "temperature": 0.9 } diff --git a/config/config.go b/config/config.go index 0c3dabc..a5557be 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "encoding/json" "log" "os" + "strconv" "sync" "time" ) @@ -21,7 +22,7 @@ type Configuration struct { // GPT模型 Model string `json:"model"` // 热度 - Temperature float32 `json:"temperature"` + Temperature float64 `json:"temperature"` } var config *Configuration @@ -30,28 +31,40 @@ var once sync.Once // LoadConfig 加载配置 func LoadConfig() *Configuration { once.Do(func() { - // 从文件中读取 + // 给配置赋默认值 config = &Configuration{ + AutoPass: false, SessionTimeout: 60, - } - f, err := os.Open("config.json") - if err != nil { - log.Fatalf("open config err: %v", err) - return - } - defer f.Close() - encoder := json.NewDecoder(f) - err = encoder.Decode(config) - if err != nil { - log.Fatalf("decode config err: %v", err) - return + MaxTokens: 512, + Model: "text-davinci-003", + Temperature: 0.9, } - // 如果环境变量有配置,读取环境变量 - ApiKey := os.Getenv("ApiKey") - AutoPass := os.Getenv("AutoPass") - SessionTimeout := os.Getenv("SessionTimeout") - if ApiKey != "" { + // 判断配置文件是否存在,存在直接JSON读取 + _, err := os.Stat("config.json") + if err == nil { + f, err := os.Open("config.json") + if err != nil { + log.Fatalf("open config err: %v", err) + return + } + defer f.Close() + encoder := json.NewDecoder(f) + err = encoder.Decode(config) + if err != nil { + log.Fatalf("decode config err: %v", err) + return + } + } + log.Println(config) + // 有环境变量使用环境变量 + ApiKey := os.Getenv("APIKEY") + AutoPass := os.Getenv("AUTO_PASS") + SessionTimeout := os.Getenv("SESSION_TIMEOUT") + Model := os.Getenv("MODEL") + MaxTokens := os.Getenv("MAX_TOKENS") + Temperature := os.Getenv("TEMPREATURE") + if ApiKey != "" { config.ApiKey = ApiKey } if AutoPass == "true" { @@ -60,11 +73,36 @@ func LoadConfig() *Configuration { if SessionTimeout != "" { duration, err := time.ParseDuration(SessionTimeout) if err != nil { - log.Fatalf("config decode session timeout err: %v ,get is %v", err, SessionTimeout) + log.Fatalf("config session timeout err: %v ,get is %v", err, SessionTimeout) return } config.SessionTimeout = duration } + if Model != "" { + config.Model = Model + } + if MaxTokens != "" { + max, err := strconv.Atoi(MaxTokens) + if err != nil { + log.Fatalf("config MaxTokens err: %v ,get is %v", err, MaxTokens) + return + } + config.MaxTokens = uint(max) + } + if Temperature != "" { + temp, err := strconv.ParseFloat(Temperature, 64) + if err != nil { + log.Fatalf("config Temperature err: %v ,get is %v", err, Temperature) + return + } + config.Temperature = temp + } }) + log.Println(config) + if config.ApiKey == "" { + log.Fatalf("config err: api key reqired") + } + + return config } diff --git a/gtp/gtp.go b/gtp/gtp.go index 471fab3..4d2b5d2 100644 --- a/gtp/gtp.go +++ b/gtp/gtp.go @@ -34,8 +34,8 @@ type ChoiceItem struct { type ChatGPTRequestBody struct { Model string `json:"model"` Prompt string `json:"prompt"` - MaxTokens int `json:"max_tokens"` - Temperature float32 `json:"temperature"` + MaxTokens uint `json:"max_tokens"` + Temperature float64 `json:"temperature"` TopP int `json:"top_p"` FrequencyPenalty int `json:"frequency_penalty"` PresencePenalty int `json:"presence_penalty"` @@ -47,11 +47,12 @@ type ChatGPTRequestBody struct { //-H "Authorization: Bearer your chatGPT key" //-d '{"model": "text-davinci-003", "prompt": "give me good song", "temperature": 0, "max_tokens": 7}' func Completions(msg string) (string, error) { + cfg := config.LoadConfig() requestBody := ChatGPTRequestBody{ - Model: "text-davinci-003", + Model: cfg.Model, Prompt: msg, - MaxTokens: 1024, - Temperature: 0.7, + MaxTokens: cfg.MaxTokens, + Temperature: cfg.Temperature, TopP: 1, FrequencyPenalty: 0, PresencePenalty: 0, @@ -77,6 +78,8 @@ func Completions(msg string) (string, error) { } defer response.Body.Close() if response.StatusCode != 200 { + body, _ := ioutil.ReadAll(response.Body) + log.Println(string(body)) return "", errors.New(fmt.Sprintf("gtp api status code not equals 200,code is %d", response.StatusCode)) } body, err := ioutil.ReadAll(response.Body) diff --git a/handlers/handler.go b/handlers/handler.go index f1bf4fb..f88647a 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -30,8 +30,10 @@ func QrCodeCallBack(uuid string) { openwechat.PrintlnQrcodeUrl(uuid) } else { log.Println("login in linux") - q, _ := qrcode.New("https://login.weixin.qq.com/l/"+uuid, qrcode.Low) - fmt.Println(q.ToString(true)) + url := "https://login.weixin.qq.com/l/" + uuid + log.Printf("如果二维码无法扫描,请尝试请复制链接到浏览器:%s", url) + q, _ := qrcode.New(url, qrcode.High) + fmt.Println(q.ToSmallString(true)) } } @@ -48,6 +50,12 @@ func init() { // Handler 全局处理入口 func Handler(msg *openwechat.Message) { + defer func() { + err := recover() + if err != nil { + log.Printf("handler recover error: %v", err) + } + }() log.Printf("hadler Received msg : %v", msg.Content) // 处理群消息 if msg.IsSendByGroup() { diff --git a/supervisord.conf b/supervisord.conf new file mode 100644 index 0000000..dba32a9 --- /dev/null +++ b/supervisord.conf @@ -0,0 +1,13 @@ +[supervisord] +nodaemon=true + +[program:wechatbot] ; 程序名称,在 supervisorctl 中通过这个值来对程序进行一系列的操作 +autorestart=True ; 程序异常退出后自动重启 +autostart=True ; 在 supervisord 启动的时候也自动启动 +redirect_stderr=True ; 把 stderr 重定向到 stdout,默认 false +command=/app/wechatbot ; 启动命令,与手动在命令行启动的命令是一样的 +user=root ; 用哪个用户启动 +stdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MB +stdout_logfile_backups = 20 ; stdout 日志文件备份数 +; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录(supervisord 会自动创建日志文件) +stdout_logfile = /app/run.log \ No newline at end of file From eca0451c7616fca9a68d1d539cbaa99fcf7aece3 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Sat, 10 Dec 2022 14:14:39 +0800 Subject: [PATCH 17/37] =?UTF-8?q?1.=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8CGPT=E8=AF=B7=E6=B1=82=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=BB=99=E7=94=A8=E6=88=B7=202.?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=93=8D=E5=BA=94=E7=BB=99=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=9A=84=E4=BF=A1=E6=81=AF=203.=E5=9B=9E=E5=A4=8D=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E5=8F=AF=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 53 +++++++++++++++++++++++++++-------- bootstrap/bootstrap.go | 9 +++--- config.dev.json | 3 +- config/config.go | 13 +++++---- gtp/gtp.go | 12 ++++---- handlers/group_msg_handler.go | 36 +++++++++++++++--------- handlers/handler.go | 9 +++--- handlers/user_msg_handler.go | 35 +++++++++++++++-------- pkg/logger/logger.go | 40 ++++++++++++++++++++++++++ 9 files changed, 155 insertions(+), 55 deletions(-) create mode 100644 pkg/logger/logger.go diff --git a/README.md b/README.md index 2899fb1..e644eda 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,46 @@ # wechatbot -> 最近chatGPT异常火爆,本项目可以将个人微信化身GPT机器人, + +> 最近ChatGPT异常火爆,本项目可以将个人微信化身GPT机器人, > 项目基于[openwechat](https://github.com/eatmoreapple/openwechat) 开发。 -[![Release](https://img.shields.io/github/v/release/869413421/wechatbot.svg?style=flat-square)](https://github.com/869413421/wechatbot/releases/tag/v1.0.1) +> `友链:`[chatgpt-dingtalk](https://github.com/eryajf/chatgpt-dingtalk) 本项目可以将GPT机器人集成到钉钉群聊中。 + +[![Release](https://img.shields.io/github/v/release/869413421/wechatbot.svg?style=flat-square)](https://github.com/869413421/wechatbot/releases/tag/v1.1.2) ![Github stars](https://img.shields.io/github/stars/869413421/wechatbot.svg) ![Forks](https://img.shields.io/github/forks/869413421/wechatbot.svg?style=flat-square) ### 目前实现了以下功能 - * GPT机器人模型热度可配置 - * 提问增加上下文,更接近官网效果 - * 机器人群聊@回复 - * 机器人私聊回复 - * 好友添加自动通过 + +* GPT机器人模型热度可配置 +* 提问增加上下文 +* 指令清空上下文(指令:我要问下一个问题) +* 机器人群聊@回复 +* 机器人私聊回复 +* 私聊回复前缀设置 +* 好友添加自动通过 + +# 实现机制 +目前机器人有两种实现方式 +* 逆向功能,扒取官网API,通过抓取cookie获取GPT响应信息,`优点:`效果与官网一致,`缺点:`cookie会过期需要不定时更新。 +* 基于openai官网提供的API,`优点`:模型以及各种参数可以自由配置,`缺点:`效果达不到官网智能,且API收费,新账号有18元免费额度。 + +> 本项目基于第二种方式实现,模型之间具体差异可以参考[官方文档](https://beta.openai.com/docs/models/overview), 详细[参数示例](https://beta.openai.com/examples) 。 + +# 常见问题 +* 如无法登录 login error: write storage.json: bad file descriptor 删除掉storage.json文件重新登录。 +* 其他无法登录问题,依然尝试删除掉storage.json文件重新登录。 +* ~~机器人无法正常回复,检查ApiKey能否正常使用,控制台日志中有详细错误信息~~ 新版本会机器人会直接输出,因为被问得好烦了。 +* linux中二维码无法扫描,缩小命令行功能,让二维码像素尽可能清晰。(无法从代码层面解决) +* 机器人一直答非所问,可能因为上下文累积过多。切换不同问题时,发送指令:我要问下一个问题。会清空上下文 # 使用前提 + > * ~~目前只支持在windows上运行因为需要弹窗扫码登录微信,后续会支持linux~~ 已支持 > * 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。 > * 微信必须实名认证。 # 注意事项 + > * 项目仅供娱乐,滥用可能有微信封禁的风险,请勿用于商业用途。 > * 请注意收发敏感信息,本项目不做信息过滤。 @@ -30,7 +52,7 @@ ```sh # 运行项目,环境变量参考下方配置说明 -$ docker run -itd --name wechatbot --restart=always -e APIKEY=xxxx -e AUTO_PASS=false -e SESSION_TIMEOUT=60s -e MODEL=text-davinci-003 -e MAX_TOKENS=512 -e TEMPREATURE=0.9 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +$ docker run -itd --name wechatbot --restart=always -e APIKEY=xxxx -e AUTO_PASS=false -e SESSION_TIMEOUT=60s -e MODEL=text-davinci-003 -e MAX_TOKENS=512 -e TEMPREATURE=0.9 -e REPLY_PREFIX=我是来自机器人回复: docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 $ docker exec -it wechatbot bash @@ -56,7 +78,9 @@ $ tail -f -n 50 /app/run.log 其中配置文件参考下边的配置文件说明。 # 快速开始 -> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.1) ,解压运行。 + +> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.2) ,解压运行。 + ```` # 获取项目 git clone https://github.com/869413421/wechatbot.git @@ -78,6 +102,7 @@ nohup ./wechatbot > run.log & ```` # 配置文件说明 + ```` { "api_key": "your api key", @@ -86,22 +111,28 @@ nohup ./wechatbot > run.log & "max_tokens": 512, "model": "text-davinci-003", "temperature": 0.9 + "reply_prefix": "来自机器人回复:" } api_key:openai api_key auto_pass:是否自动通过好友添加 session_timeout:会话超时时间,默认60秒,单位秒,在会话时间内所有发送给机器人的信息会作为上下文。 max_tokens: GPT响应字符数,最大2048,默认值512。max_tokens会影响接口响应速度,字符越大响应越慢。 -model: GTP选用模型,默认text-davinci-003,具体选项参考官网训练场 -temperature: GTP热度,0到1,默认0.9。数字越大创造力越强,但更偏离训练事实,越低越接近训练事实 +model: GPT选用模型,默认text-davinci-003,具体选项参考官网训练场 +temperature: GPT热度,0到1,默认0.9。数字越大创造力越强,但更偏离训练事实,越低越接近训练事实 +reply_prefix: 私聊回复前缀 ```` # 使用示例 + ### 向机器人发送`我要问下一个问题`,清空会话信息。 + ### 私聊 + ### 群聊@回复 + ### 添加微信(备注: wechabot)进群交流 diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 924ace9..da3cc80 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -1,9 +1,10 @@ package bootstrap import ( + "fmt" "github.com/869413421/wechatbot/handlers" + "github.com/869413421/wechatbot/pkg/logger" "github.com/eatmoreapple/openwechat" - "log" ) @@ -23,10 +24,8 @@ func Run() { // 执行热登录 err := bot.HotLogin(reloadStorage) if err != nil { - if err = bot.Login(); err != nil { - log.Printf("login error: %v \n", err) - return - } + logger.Warning(fmt.Sprintf("login error: %v ", err)) + return } // 阻塞主goroutine, 直到发生异常或者用户主动退出 bot.Block() diff --git a/config.dev.json b/config.dev.json index e66f3ec..06a0b30 100644 --- a/config.dev.json +++ b/config.dev.json @@ -4,5 +4,6 @@ "session_timeout": 60, "max_tokens": 512, "model": "text-davinci-003", - "temperature": 0.9 + "temperature": 0.9, + "reply_prefix": "来自机器人回复:" } diff --git a/config/config.go b/config/config.go index a5557be..122ddf5 100644 --- a/config/config.go +++ b/config/config.go @@ -23,6 +23,8 @@ type Configuration struct { Model string `json:"model"` // 热度 Temperature float64 `json:"temperature"` + // 回复前缀 + ReplyPrefix string `json:"reply_prefix"` } var config *Configuration @@ -56,7 +58,6 @@ func LoadConfig() *Configuration { return } } - log.Println(config) // 有环境变量使用环境变量 ApiKey := os.Getenv("APIKEY") AutoPass := os.Getenv("AUTO_PASS") @@ -64,7 +65,8 @@ func LoadConfig() *Configuration { Model := os.Getenv("MODEL") MaxTokens := os.Getenv("MAX_TOKENS") Temperature := os.Getenv("TEMPREATURE") - if ApiKey != "" { + ReplyPrefix := os.Getenv("REPLY_PREFIX") + if ApiKey != "" { config.ApiKey = ApiKey } if AutoPass == "true" { @@ -97,12 +99,13 @@ func LoadConfig() *Configuration { } config.Temperature = temp } + if ReplyPrefix != "" { + config.ReplyPrefix = ReplyPrefix + } }) - log.Println(config) - if config.ApiKey == "" { + if config.ApiKey == "" { log.Fatalf("config err: api key reqired") } - return config } diff --git a/gtp/gtp.go b/gtp/gtp.go index 4d2b5d2..f1351ae 100644 --- a/gtp/gtp.go +++ b/gtp/gtp.go @@ -6,9 +6,11 @@ import ( "errors" "fmt" "github.com/869413421/wechatbot/config" + "github.com/869413421/wechatbot/pkg/logger" "io/ioutil" "log" "net/http" + "time" ) const BASEURL = "https://api.openai.com/v1/" @@ -62,7 +64,7 @@ func Completions(msg string) (string, error) { if err != nil { return "", err } - log.Printf("request gtp json string : %v", string(requestData)) + logger.Info(fmt.Sprintf("request gtp json string : %v", string(requestData))) req, err := http.NewRequest("POST", BASEURL+"completions", bytes.NewBuffer(requestData)) if err != nil { return "", err @@ -71,7 +73,7 @@ func Completions(msg string) (string, error) { apiKey := config.LoadConfig().ApiKey req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+apiKey) - client := &http.Client{} + client := &http.Client{Timeout: 30 * time.Second} response, err := client.Do(req) if err != nil { return "", err @@ -79,13 +81,13 @@ func Completions(msg string) (string, error) { defer response.Body.Close() if response.StatusCode != 200 { body, _ := ioutil.ReadAll(response.Body) - log.Println(string(body)) - return "", errors.New(fmt.Sprintf("gtp api status code not equals 200,code is %d", response.StatusCode)) + return "", errors.New(fmt.Sprintf("请求GTP出错了,gtp api status code not equals 200,code is %d ,details: %v ", response.StatusCode, string(body))) } body, err := ioutil.ReadAll(response.Body) if err != nil { return "", err } + logger.Info(fmt.Sprintf("response gtp json string : %v", string(body))) gptResponseBody := &ChatGPTResponseBody{} log.Println(string(body)) @@ -98,6 +100,6 @@ func Completions(msg string) (string, error) { if len(gptResponseBody.Choices) > 0 { reply = gptResponseBody.Choices[0].Text } - log.Printf("gpt response text: %s \n", reply) + logger.Info(fmt.Sprintf("gpt response text: %s ", reply)) return reply, nil } diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index a484bc7..e31c254 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -1,9 +1,11 @@ package handlers import ( + "errors" + "fmt" "github.com/869413421/wechatbot/gtp" + "github.com/869413421/wechatbot/pkg/logger" "github.com/eatmoreapple/openwechat" - "log" "strings" ) @@ -31,7 +33,7 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { // 接收群消息 sender, err := msg.Sender() group := openwechat.Group{User: sender} - log.Printf("Received Group %v Text Msg : %v", group.NickName, msg.Content) + logger.Info(fmt.Sprintf("Received Group %v Text Msg : %v", group.NickName, msg.Content)) // 不是@的不处理 if !msg.IsAt() { @@ -41,15 +43,14 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { // 获取@我的用户 groupSender, err := msg.SenderInGroup() if err != nil { - log.Printf("get sender in group error :%v \n", err) - return err + return errors.New(fmt.Sprintf("get sender in group error :%v ", err)) } atText := "@" + groupSender.NickName + " " if UserService.ClearUserSessionContext(sender.ID(), msg.Content) { _, err = msg.ReplyText(atText + "上下文已经清空了,你可以问下一个问题啦。") if err != nil { - log.Printf("response user error: %v \n", err) + return errors.New(fmt.Sprintf("response user error: %v", err)) } return nil } @@ -61,10 +62,11 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { } reply, err := gtp.Completions(requestText) if err != nil { - log.Printf("gtp request error: %v \n", err) - _, err = msg.ReplyText("机器人神了,我一会发现了就去修。") + // 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 + errMsg := fmt.Sprintf("gtp request error: %v", err) + _, err = msg.ReplyText(errMsg) if err != nil { - log.Printf("response group error: %v \n", err) + return errors.New(fmt.Sprintf("response group error: %v ", err)) } return err } @@ -72,19 +74,27 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { return nil } - // 回复@我的用户 - reply = strings.TrimSpace(reply) - reply = strings.Trim(reply, "\n") // 设置上下文 UserService.SetUserSessionContext(sender.ID(), requestText, reply) - replyText := atText + reply + replyText := atText + buildGroupReply(reply) _, err = msg.ReplyText(replyText) if err != nil { - log.Printf("response group error: %v \n", err) + return errors.New(fmt.Sprintf("response group error: %v ", err)) } return err } +// buildUserReply 构建用户回复 +func buildGroupReply(reply string) string { + // 回复@我的用户 + reply = strings.Trim(strings.Trim(reply, "?"), "\n") + if reply == "" { + return "请求得不到任何有意义的回复,请具体提出问题。" + } + reply = strings.Trim(reply, "\n") + return reply +} + // buildRequestText 构建请求GPT的文本,替换掉机器人名称,然后检查是否有上下文,如果有拼接上 func buildRequestText(sender *openwechat.User, msg *openwechat.Message) string { replaceText := "@" + sender.Self.NickName diff --git a/handlers/handler.go b/handlers/handler.go index f88647a..665b0c8 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -3,6 +3,7 @@ package handlers import ( "fmt" "github.com/869413421/wechatbot/config" + "github.com/869413421/wechatbot/pkg/logger" "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" "github.com/skip2/go-qrcode" @@ -53,10 +54,10 @@ func Handler(msg *openwechat.Message) { defer func() { err := recover() if err != nil { - log.Printf("handler recover error: %v", err) + logger.Warning(fmt.Sprintf("handler recover error: %v", err)) } }() - log.Printf("hadler Received msg : %v", msg.Content) + // 处理群消息 if msg.IsSendByGroup() { handlers[GroupHandler].handle(msg) @@ -66,9 +67,9 @@ func Handler(msg *openwechat.Message) { // 好友申请 if msg.IsFriendAdd() { if config.LoadConfig().AutoPass { - _, err := msg.Agree("你好我是基于chatGPT引擎开发的微信机器人,你可以向我提问任何问题。") + _, err := msg.Agree("") if err != nil { - log.Fatalf("add friend agree error : %v", err) + logger.Warning(fmt.Sprintf("add friend agree error : %v", err)) return } } diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 3372e6f..593db0f 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -1,9 +1,12 @@ package handlers import ( + "errors" + "fmt" + "github.com/869413421/wechatbot/config" "github.com/869413421/wechatbot/gtp" + "github.com/869413421/wechatbot/pkg/logger" "github.com/eatmoreapple/openwechat" - "log" "strings" ) @@ -30,11 +33,11 @@ func NewUserMessageHandler() MessageHandlerInterface { func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { // 接收私聊消息 sender, err := msg.Sender() - log.Printf("Received User %v Text Msg : %v", sender.NickName, msg.Content) + logger.Info(fmt.Sprintf("Received User %v Text Msg : %v", sender.NickName, msg.Content)) if UserService.ClearUserSessionContext(sender.ID(), msg.Content) { _, err = msg.ReplyText("上下文已经清空了,你可以问下一个问题啦。") if err != nil { - log.Printf("response user error: %v \n", err) + return errors.New(fmt.Sprintf("response user error: %v", err)) } return nil } @@ -42,12 +45,14 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { // 获取上下文,向GPT发起请求 requestText := strings.TrimSpace(msg.Content) requestText = strings.Trim(msg.Content, "\n") - requestText = UserService.GetUserSessionContext(sender.ID()) + requestText reply, err := gtp.Completions(requestText) if err != nil { - log.Printf("gtp request error: %v \n", err) - msg.ReplyText("机器人神了,我一会发现了就去修。") + errMsg := fmt.Sprintf("gtp request error: %v", err) + _, err = msg.ReplyText(errMsg) + if err != nil { + return errors.New(fmt.Sprintf("response user error: %v ", err)) + } return err } if reply == "" { @@ -55,13 +60,21 @@ func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { } // 设置上下文,回复用户 - reply = strings.TrimSpace(reply) - reply = strings.Trim(reply, "\n") UserService.SetUserSessionContext(sender.ID(), requestText, reply) - reply = "本消息由ChatGPTBot回复:\n" + reply - _, err = msg.ReplyText(reply) + _, err = msg.ReplyText(buildUserReply(reply)) if err != nil { - log.Printf("response user error: %v \n", err) + return errors.New(fmt.Sprintf("response user error: %v ", err)) } return err } + +// buildUserReply 构建用户回复 +func buildUserReply(reply string) string { + reply = strings.Trim(strings.Trim(reply, "?"), "\n") + if reply == "" { + return "请求得不到任何有意义的回复,请具体提出问题。" + } + reply = config.LoadConfig().ReplyPrefix + "\n" + reply + reply = strings.Trim(reply, "\n") + return reply +} diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go new file mode 100644 index 0000000..7a3955d --- /dev/null +++ b/pkg/logger/logger.go @@ -0,0 +1,40 @@ +package logger + +import ( + "log" + "os" + "sync" +) + +var Logger *log.Logger +var once sync.Once + +func init() { + once.Do(func() { + Logger = log.New(os.Stdout, "INFO", log.Ldate|log.Ltime|log.Lshortfile) + }) +} + +// Info 详情 +func Info(args ...interface{}) { + Logger.SetPrefix("[INFO]") + Logger.Println(args...) +} + +// Danger 错误 为什么不命名为 error?避免和 error 类型重名 +func Danger(args ...interface{}) { + Logger.SetPrefix("[ERROR]") + Logger.Fatal(args...) +} + +// Warning 警告 +func Warning(args ...interface{}) { + Logger.SetPrefix("[WARNING]") + Logger.Println(args...) +} + +// DeBug debug +func DeBug(args ...interface{}) { + Logger.SetPrefix("[DeBug]") + Logger.Println(args...) +} From 8af217fe362d6f90dae6c0882b4a358bc648e930 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Sat, 10 Dec 2022 15:42:19 +0800 Subject: [PATCH 18/37] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BE=A4=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87=E6=97=A0=E6=B3=95=E6=B8=85=E7=A9=BAbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/group_msg_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index e31c254..b7d350a 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -47,7 +47,7 @@ func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { } atText := "@" + groupSender.NickName + " " - if UserService.ClearUserSessionContext(sender.ID(), msg.Content) { + if UserService.ClearUserSessionContext(groupSender.ID(), msg.Content) { _, err = msg.ReplyText(atText + "上下文已经清空了,你可以问下一个问题啦。") if err != nil { return errors.New(fmt.Sprintf("response user error: %v", err)) From 70d4b20b4d984a7938d8d5f56009b99ab397a36a Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Sat, 10 Dec 2022 16:49:16 +0800 Subject: [PATCH 19/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.dev.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config.dev.json b/config.dev.json index 06a0b30..a343af6 100644 --- a/config.dev.json +++ b/config.dev.json @@ -1,9 +1,11 @@ { "api_key": "your api key", - "auto_pass": false, + "auto_pass": true, "session_timeout": 60, "max_tokens": 512, "model": "text-davinci-003", - "temperature": 0.9, + "temperature": 1, "reply_prefix": "来自机器人回复:" } + + From 5092d2ab2a5cb756da50f5933261e5e07d89cebb Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Sat, 10 Dec 2022 16:52:08 +0800 Subject: [PATCH 20/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.dev.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config.dev.json b/config.dev.json index a343af6..f988af0 100644 --- a/config.dev.json +++ b/config.dev.json @@ -1,5 +1,5 @@ { - "api_key": "your api key", + "api_key": "api key", "auto_pass": true, "session_timeout": 60, "max_tokens": 512, @@ -7,5 +7,3 @@ "temperature": 1, "reply_prefix": "来自机器人回复:" } - - From efb539f225b95b0f4bc1833bdaf5e507aec1c2e5 Mon Sep 17 00:00:00 2001 From: lafeier <812747475@qq.com> Date: Sat, 10 Dec 2022 21:29:24 +0800 Subject: [PATCH 21/37] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=B0=91=E4=B8=AA=E9=80=97=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e644eda..b365022 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ nohup ./wechatbot > run.log & "session_timeout": 60, "max_tokens": 512, "model": "text-davinci-003", - "temperature": 0.9 + "temperature": 0.9, "reply_prefix": "来自机器人回复:" } From ac478c811c474bb821c8d1b703b6cbd498b1dcd5 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Sat, 10 Dec 2022 21:43:41 +0800 Subject: [PATCH 22/37] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=83=AD=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bootstrap/bootstrap.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index da3cc80..22c3e5f 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -22,7 +22,7 @@ func Run() { reloadStorage := openwechat.NewJsonFileHotReloadStorage("storage.json") // 执行热登录 - err := bot.HotLogin(reloadStorage) + err := bot.HotLogin(reloadStorage,true) if err != nil { logger.Warning(fmt.Sprintf("login error: %v ", err)) return From 1a0adcfa21bd2e24b75a3d1e6b874d5d5df402ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Sun, 11 Dec 2022 12:08:17 +0800 Subject: [PATCH 23/37] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=98=A0=E5=B0=84=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b365022..41eb4a3 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ $ tail -f -n 50 /app/run.log cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录获取 # 运行项目 -docker run -itd --name wechatbot -v ./config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +docker run -itd --name wechatbot -v `pwd`/config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 $ docker exec -it wechatbot bash From afec2925b933a44026e835e23170b8485e9f824f Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Sun, 11 Dec 2022 19:05:57 +0800 Subject: [PATCH 24/37] =?UTF-8?q?1.=E9=87=8D=E6=9E=84=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=EF=BC=8C=E5=A2=9E=E5=8A=A0=E9=98=85=E8=AF=BB?= =?UTF-8?q?=E6=80=A7=202.=E7=BE=A4=E8=81=8A=E5=A2=9E=E5=8A=A0=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E5=9B=9E=E5=A4=8D=203.=E4=BF=AE=E5=A4=8D=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E8=BF=87=E9=95=BF=E5=BC=95=E8=B5=B7GPT=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=A4=B1=E8=B4=A5BUG=204.=E6=B8=85=E7=A9=BA=E4=BC=9A?= =?UTF-8?q?=E8=AF=9D=E5=8F=A3=E4=BB=A4=E6=9B=B4=E6=94=B9=E4=B8=BA=E5=8F=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++--- config.dev.json | 7 +- config/config.go | 27 ++++-- handlers/group_msg_handler.go | 153 +++++++++++++++++++++------------- handlers/handler.go | 72 ++++++++++------ handlers/token_msg_handler.go | 57 +++++++++++++ handlers/user_msg_handler.go | 100 +++++++++++++++------- service/user.go | 47 ++++++----- 8 files changed, 333 insertions(+), 155 deletions(-) create mode 100644 handlers/token_msg_handler.go diff --git a/README.md b/README.md index b365022..503b052 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ > `友链:`[chatgpt-dingtalk](https://github.com/eryajf/chatgpt-dingtalk) 本项目可以将GPT机器人集成到钉钉群聊中。 -[![Release](https://img.shields.io/github/v/release/869413421/wechatbot.svg?style=flat-square)](https://github.com/869413421/wechatbot/releases/tag/v1.1.2) +[![Release](https://img.shields.io/github/v/release/869413421/wechatbot.svg?style=flat-square)](https://github.com/869413421/wechatbot/releases/tag/v1.1.3) ![Github stars](https://img.shields.io/github/stars/869413421/wechatbot.svg) ![Forks](https://img.shields.io/github/forks/869413421/wechatbot.svg?style=flat-square) @@ -52,7 +52,7 @@ ```sh # 运行项目,环境变量参考下方配置说明 -$ docker run -itd --name wechatbot --restart=always -e APIKEY=xxxx -e AUTO_PASS=false -e SESSION_TIMEOUT=60s -e MODEL=text-davinci-003 -e MAX_TOKENS=512 -e TEMPREATURE=0.9 -e REPLY_PREFIX=我是来自机器人回复: docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +$ docker run -itd --name wechatbot --restart=always -e APIKEY=换成你的key -e AUTO_PASS=false -e SESSION_TIMEOUT=60s -e MODEL=text-davinci-003 -e MAX_TOKENS=512 -e TEMPREATURE=0.9 -e REPLY_PREFIX=我是来自机器人回复: -e SESSION_CLEAR_TOKEN=下一个问题 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 $ docker exec -it wechatbot bash @@ -79,7 +79,7 @@ $ tail -f -n 50 /app/run.log # 快速开始 -> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.2) ,解压运行。 +> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.3) ,解压运行。 ```` # 获取项目 @@ -99,6 +99,8 @@ go run main.go CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o wechatbot ./main.go # 守护进程运行 nohup ./wechatbot > run.log & +# 查看二维码 +# tail -f -n 50 run.log ```` # 配置文件说明 @@ -106,12 +108,13 @@ nohup ./wechatbot > run.log & ```` { "api_key": "your api key", - "auto_pass": false, + "auto_pass": true, "session_timeout": 60, - "max_tokens": 512, + "max_tokens": 1024, "model": "text-davinci-003", - "temperature": 0.9, - "reply_prefix": "来自机器人回复:" + "temperature": 1, + "reply_prefix": "来自机器人回复:", + "session_clear_token": "清空会话" } api_key:openai api_key @@ -121,11 +124,12 @@ max_tokens: GPT响应字符数,最大2048,默认值512。max_tokens会影响 model: GPT选用模型,默认text-davinci-003,具体选项参考官网训练场 temperature: GPT热度,0到1,默认0.9。数字越大创造力越强,但更偏离训练事实,越低越接近训练事实 reply_prefix: 私聊回复前缀 +session_clear_token: 会话清空口令,默认`下一个问题` ```` # 使用示例 -### 向机器人发送`我要问下一个问题`,清空会话信息。 +### 向机器人发送`下一个问题`,清空会话信息。 ### 私聊 @@ -135,9 +139,4 @@ reply_prefix: 私聊回复前缀 -### 添加微信(备注: wechabot)进群交流 - -**如果二维码图片没显示出来,请添加微信号 huangyanming681925** - - diff --git a/config.dev.json b/config.dev.json index f988af0..84bf81c 100644 --- a/config.dev.json +++ b/config.dev.json @@ -1,9 +1,10 @@ { - "api_key": "api key", + "api_key": "your api key", "auto_pass": true, "session_timeout": 60, - "max_tokens": 512, + "max_tokens": 1024, "model": "text-davinci-003", "temperature": 1, - "reply_prefix": "来自机器人回复:" + "reply_prefix": "来自机器人回复:", + "session_clear_token": "清空会话" } diff --git a/config/config.go b/config/config.go index 122ddf5..c4f6d2e 100644 --- a/config/config.go +++ b/config/config.go @@ -2,6 +2,8 @@ package config import ( "encoding/json" + "fmt" + "github.com/869413421/wechatbot/pkg/logger" "log" "os" "strconv" @@ -25,6 +27,8 @@ type Configuration struct { Temperature float64 `json:"temperature"` // 回复前缀 ReplyPrefix string `json:"reply_prefix"` + // 清空会话口令 + SessionClearToken string `json:"session_clear_token"` } var config *Configuration @@ -35,11 +39,12 @@ func LoadConfig() *Configuration { once.Do(func() { // 给配置赋默认值 config = &Configuration{ - AutoPass: false, - SessionTimeout: 60, - MaxTokens: 512, - Model: "text-davinci-003", - Temperature: 0.9, + AutoPass: false, + SessionTimeout: 60, + MaxTokens: 512, + Model: "text-davinci-003", + Temperature: 0.9, + SessionClearToken: "下一个问题", } // 判断配置文件是否存在,存在直接JSON读取 @@ -66,6 +71,7 @@ func LoadConfig() *Configuration { MaxTokens := os.Getenv("MAX_TOKENS") Temperature := os.Getenv("TEMPREATURE") ReplyPrefix := os.Getenv("REPLY_PREFIX") + SessionClearToken := os.Getenv("SESSION_CLEAR_TOKEN") if ApiKey != "" { config.ApiKey = ApiKey } @@ -75,7 +81,7 @@ func LoadConfig() *Configuration { if SessionTimeout != "" { duration, err := time.ParseDuration(SessionTimeout) if err != nil { - log.Fatalf("config session timeout err: %v ,get is %v", err, SessionTimeout) + logger.Danger(fmt.Sprintf("config session timeout err: %v ,get is %v", err, SessionTimeout)) return } config.SessionTimeout = duration @@ -86,7 +92,7 @@ func LoadConfig() *Configuration { if MaxTokens != "" { max, err := strconv.Atoi(MaxTokens) if err != nil { - log.Fatalf("config MaxTokens err: %v ,get is %v", err, MaxTokens) + logger.Danger(fmt.Sprintf("config MaxTokens err: %v ,get is %v", err, MaxTokens)) return } config.MaxTokens = uint(max) @@ -94,7 +100,7 @@ func LoadConfig() *Configuration { if Temperature != "" { temp, err := strconv.ParseFloat(Temperature, 64) if err != nil { - log.Fatalf("config Temperature err: %v ,get is %v", err, Temperature) + logger.Danger(fmt.Sprintf("config Temperature err: %v ,get is %v", err, Temperature)) return } config.Temperature = temp @@ -102,9 +108,12 @@ func LoadConfig() *Configuration { if ReplyPrefix != "" { config.ReplyPrefix = ReplyPrefix } + if SessionClearToken != "" { + config.SessionClearToken = SessionClearToken + } }) if config.ApiKey == "" { - log.Fatalf("config err: api key reqired") + logger.Danger("config err: api key required") } return config diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index b7d350a..9fec613 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/869413421/wechatbot/gtp" "github.com/869413421/wechatbot/pkg/logger" + "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" "strings" ) @@ -13,95 +14,133 @@ var _ MessageHandlerInterface = (*GroupMessageHandler)(nil) // GroupMessageHandler 群消息处理 type GroupMessageHandler struct { -} - -// handle 处理消息 -func (g *GroupMessageHandler) handle(msg *openwechat.Message) error { - if msg.IsText() { - return g.ReplyText(msg) - } - return nil + // 获取自己 + self *openwechat.Self + // 群 + group *openwechat.Group + // 接收到消息 + msg *openwechat.Message + // 发送的用户 + sender *openwechat.User + // 实现的用户业务 + service service.UserServiceInterface } // NewGroupMessageHandler 创建群消息处理器 -func NewGroupMessageHandler() MessageHandlerInterface { - return &GroupMessageHandler{} -} - -// ReplyText 发送文本消息到群 -func (g *GroupMessageHandler) ReplyText(msg *openwechat.Message) error { - // 接收群消息 +func NewGroupMessageHandler(msg *openwechat.Message) (MessageHandlerInterface, error) { sender, err := msg.Sender() - group := openwechat.Group{User: sender} - logger.Info(fmt.Sprintf("Received Group %v Text Msg : %v", group.NickName, msg.Content)) - - // 不是@的不处理 - if !msg.IsAt() { - return nil + if err != nil { + return nil, err } - - // 获取@我的用户 + group := &openwechat.Group{User: sender} groupSender, err := msg.SenderInGroup() if err != nil { - return errors.New(fmt.Sprintf("get sender in group error :%v ", err)) + return nil, err } - atText := "@" + groupSender.NickName + " " - if UserService.ClearUserSessionContext(groupSender.ID(), msg.Content) { - _, err = msg.ReplyText(atText + "上下文已经清空了,你可以问下一个问题啦。") - if err != nil { - return errors.New(fmt.Sprintf("response user error: %v", err)) - } + userService := service.NewUserService(c, groupSender) + handler := &GroupMessageHandler{ + self: sender.Self, + msg: msg, + group: group, + sender: groupSender, + service: userService, + } + return handler, nil + +} + +// handle 处理消息 +func (g *GroupMessageHandler) handle() error { + if g.msg.IsText() { + return g.ReplyText() + } + return nil +} + +// ReplyText 发送文本消息到群 +func (g *GroupMessageHandler) ReplyText() error { + logger.Info(fmt.Sprintf("Received Group %v Text Msg : %v", g.group.NickName, g.msg.Content)) + // 1.不是@的不处理 + if !g.msg.IsAt() { return nil } - // 替换掉@文本,设置会话上下文,然后向GPT发起请求。 - requestText := buildRequestText(sender, msg) + // 2.获取请求的文本,如果为空字符串不处理 + requestText := g.getRequestText() if requestText == "" { + logger.Info("user message is null") return nil } + + // 3.请求GPT获取回复 reply, err := gtp.Completions(requestText) if err != nil { - // 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 + // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 errMsg := fmt.Sprintf("gtp request error: %v", err) - _, err = msg.ReplyText(errMsg) + _, err = g.msg.ReplyText(errMsg) if err != nil { return errors.New(fmt.Sprintf("response group error: %v ", err)) } return err } - if reply == "" { - return nil - } - // 设置上下文 - UserService.SetUserSessionContext(sender.ID(), requestText, reply) - replyText := atText + buildGroupReply(reply) - _, err = msg.ReplyText(replyText) + // 4.设置上下文,并响应信息给用户 + selfName := "@" + g.self.NickName + question := strings.ReplaceAll(g.msg.Content, selfName, "") + g.service.SetUserSessionContext(question, reply) + _, err = g.msg.ReplyText(g.buildReplyText(reply)) if err != nil { - return errors.New(fmt.Sprintf("response group error: %v ", err)) + return errors.New(fmt.Sprintf("response user error: %v ", err)) } + + // 5.返回错误信息 return err } -// buildUserReply 构建用户回复 -func buildGroupReply(reply string) string { - // 回复@我的用户 - reply = strings.Trim(strings.Trim(reply, "?"), "\n") - if reply == "" { - return "请求得不到任何有意义的回复,请具体提出问题。" - } - reply = strings.Trim(reply, "\n") - return reply -} +// getRequestText 获取请求接口的文本,要做一些清洗 +func (g *GroupMessageHandler) getRequestText() string { + // 1.去除空格以及换行 + requestText := strings.TrimSpace(g.msg.Content) + requestText = strings.Trim(g.msg.Content, "\n") -// buildRequestText 构建请求GPT的文本,替换掉机器人名称,然后检查是否有上下文,如果有拼接上 -func buildRequestText(sender *openwechat.User, msg *openwechat.Message) string { - replaceText := "@" + sender.Self.NickName - requestText := strings.TrimSpace(strings.ReplaceAll(msg.Content, replaceText, "")) + // 2.替换掉当前用户名称 + replaceText := "@" + g.self.NickName + requestText = strings.TrimSpace(strings.ReplaceAll(g.msg.Content, replaceText, "")) if requestText == "" { return "" } - requestText = UserService.GetUserSessionContext(sender.ID()) + requestText + + // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算) + requestText = g.service.GetUserSessionContext() + requestText + if len(requestText) >= 4000 { + requestText = requestText[:4000] + } + + // 4.返回请求文本 return requestText } + +// buildReply 构建回复文本 +func (g *GroupMessageHandler) buildReplyText(reply string) string { + // 1.获取@我的用户 + atText := "@" + g.sender.NickName + textSplit := strings.Split(reply, "\n\n") + if len(textSplit) > 1 { + trimText := textSplit[0] + reply = strings.Trim(reply, trimText) + } + reply = strings.TrimSpace(reply) + if reply == "" { + return atText + " 请求得不到任何有意义的回复,请具体提出问题。" + } + + // 2.拼接回复,@我的用户,问题,回复 + replaceText := "@" + g.self.NickName + question := strings.TrimSpace(strings.ReplaceAll(g.msg.Content, replaceText, "")) + reply = atText + "\n" + question + "\n --------------------------------\n" + reply + reply = strings.Trim(reply, "\n") + + // 3.返回回复的内容 + return reply +} diff --git a/handlers/handler.go b/handlers/handler.go index 665b0c8..6d825ad 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -4,26 +4,23 @@ import ( "fmt" "github.com/869413421/wechatbot/config" "github.com/869413421/wechatbot/pkg/logger" - "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" + "github.com/patrickmn/go-cache" "github.com/skip2/go-qrcode" "log" "runtime" + "strings" + "time" ) +var c = cache.New(config.LoadConfig().SessionTimeout, time.Minute*5) + // MessageHandlerInterface 消息处理接口 type MessageHandlerInterface interface { - handle(*openwechat.Message) error - ReplyText(*openwechat.Message) error + handle() error + ReplyText() error } -type HandlerType string - -const ( - GroupHandler = "group" - UserHandler = "user" -) - // QrCodeCallBack 登录扫码回调, func QrCodeCallBack(uuid string) { if runtime.GOOS == "windows" { @@ -32,23 +29,12 @@ func QrCodeCallBack(uuid string) { } else { log.Println("login in linux") url := "https://login.weixin.qq.com/l/" + uuid - log.Printf("如果二维码无法扫描,请尝试请复制链接到浏览器:%s", url) + log.Printf("如果二维码无法扫描,请缩小控制台尺寸,或更换命令行工具,缩小二维码像素") q, _ := qrcode.New(url, qrcode.High) fmt.Println(q.ToSmallString(true)) } } -// handlers 所有消息类型类型的处理器 -var handlers map[HandlerType]MessageHandlerInterface -var UserService service.UserServiceInterface - -func init() { - handlers = make(map[HandlerType]MessageHandlerInterface) - handlers[GroupHandler] = NewGroupMessageHandler() - handlers[UserHandler] = NewUserMessageHandler() - UserService = service.NewUserService() -} - // Handler 全局处理入口 func Handler(msg *openwechat.Message) { defer func() { @@ -58,9 +44,36 @@ func Handler(msg *openwechat.Message) { } }() + // 清空会话 + if strings.Contains(msg.Content, config.LoadConfig().SessionClearToken) { + // 获取口令消息处理器 + handler, err := NewTokenMessageHandler(msg) + if err != nil { + logger.Warning(fmt.Sprintf("init token message handler error: %s", err)) + } + + // 获取口令消息处理器 + err = handler.handle() + if err != nil { + logger.Warning(fmt.Sprintf("handle token message error: %s", err)) + } + return + } + // 处理群消息 if msg.IsSendByGroup() { - handlers[GroupHandler].handle(msg) + // 获取用户消息处理器 + handler, err := NewGroupMessageHandler(msg) + if err != nil { + logger.Warning(fmt.Sprintf("init group message handler error: %s", err)) + return + } + + // 处理用户消息 + err = handler.handle() + if err != nil { + logger.Warning(fmt.Sprintf("handle group message error: %s", err)) + } return } @@ -76,5 +89,16 @@ func Handler(msg *openwechat.Message) { } // 私聊 - handlers[UserHandler].handle(msg) + // 获取用户消息处理器 + handler, err := NewUserMessageHandler(msg) + if err != nil { + logger.Warning(fmt.Sprintf("init user message handler error: %s", err)) + } + + // 处理用户消息 + err = handler.handle() + if err != nil { + logger.Warning(fmt.Sprintf("handle user message error: %s", err)) + } + return } diff --git a/handlers/token_msg_handler.go b/handlers/token_msg_handler.go new file mode 100644 index 0000000..7a75111 --- /dev/null +++ b/handlers/token_msg_handler.go @@ -0,0 +1,57 @@ +package handlers + +import ( + "github.com/869413421/wechatbot/pkg/logger" + "github.com/869413421/wechatbot/service" + "github.com/eatmoreapple/openwechat" +) + +var _ MessageHandlerInterface = (*TokenMessageHandler)(nil) + +// TokenMessageHandler 口令消息处理器 +type TokenMessageHandler struct { + // 接收到消息 + msg *openwechat.Message + // 发送的用户 + sender *openwechat.User + // 实现的用户业务 + service service.UserServiceInterface +} + +// NewTokenMessageHandler 口令消息处理器 +func NewTokenMessageHandler(msg *openwechat.Message) (MessageHandlerInterface, error) { + sender, err := msg.Sender() + if err != nil { + return nil, err + } + if msg.IsComeFromGroup() { + sender, err = msg.SenderInGroup() + } + userService := service.NewUserService(c, sender) + handler := &TokenMessageHandler{ + msg: msg, + sender: sender, + service: userService, + } + + return handler, nil +} + +// handle 处理口令 +func (t *TokenMessageHandler) handle() error { + return t.ReplyText() +} + +// ReplyText 回复清空口令 +func (t *TokenMessageHandler) ReplyText() error { + logger.Info("user clear token") + t.service.ClearUserSessionContext() + var err error + if t.msg.IsComeFromGroup() { + atText := "@" + t.sender.NickName + "上下文已经清空,请问下一个问题。" + _, err = t.msg.ReplyText(atText) + } else { + _, err = t.msg.ReplyText("上下文已经清空,请问下一个问题。") + } + return err +} diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 593db0f..3190829 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -6,6 +6,7 @@ import ( "github.com/869413421/wechatbot/config" "github.com/869413421/wechatbot/gtp" "github.com/869413421/wechatbot/pkg/logger" + "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" "strings" ) @@ -14,67 +15,106 @@ var _ MessageHandlerInterface = (*UserMessageHandler)(nil) // UserMessageHandler 私聊消息处理 type UserMessageHandler struct { + // 接收到消息 + msg *openwechat.Message + // 发送的用户 + sender *openwechat.User + // 实现的用户业务 + service service.UserServiceInterface } -// handle 处理消息 -func (g *UserMessageHandler) handle(msg *openwechat.Message) error { - if msg.IsText() { - return g.ReplyText(msg) +// NewUserMessageHandler 创建私聊处理器 +func NewUserMessageHandler(message *openwechat.Message) (MessageHandlerInterface, error) { + sender, err := message.Sender() + if err != nil { + return nil, err } - return nil + userService := service.NewUserService(c, sender) + handler := &UserMessageHandler{ + msg: message, + sender: sender, + service: userService, + } + + return handler, nil } -// NewUserMessageHandler 创建私聊处理器 -func NewUserMessageHandler() MessageHandlerInterface { - return &UserMessageHandler{} +// handle 处理消息 +func (h *UserMessageHandler) handle() error { + if h.msg.IsText() { + return h.ReplyText() + } + return nil } // ReplyText 发送文本消息到群 -func (g *UserMessageHandler) ReplyText(msg *openwechat.Message) error { - // 接收私聊消息 - sender, err := msg.Sender() - logger.Info(fmt.Sprintf("Received User %v Text Msg : %v", sender.NickName, msg.Content)) - if UserService.ClearUserSessionContext(sender.ID(), msg.Content) { - _, err = msg.ReplyText("上下文已经清空了,你可以问下一个问题啦。") - if err != nil { - return errors.New(fmt.Sprintf("response user error: %v", err)) - } +func (h *UserMessageHandler) ReplyText() error { + logger.Info(fmt.Sprintf("Received User %v Text Msg : %v", h.sender.NickName, h.msg.Content)) + // 1.获取上下文,如果字符串为空不处理 + requestText := h.getRequestText() + if requestText == "" { + logger.Info("user message is null") return nil } - // 获取上下文,向GPT发起请求 - requestText := strings.TrimSpace(msg.Content) - requestText = strings.Trim(msg.Content, "\n") - requestText = UserService.GetUserSessionContext(sender.ID()) + requestText - reply, err := gtp.Completions(requestText) + // 2.向GPT发起请求,如果回复文本等于空,不回复 + reply, err := gtp.Completions(h.getRequestText()) if err != nil { + // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 errMsg := fmt.Sprintf("gtp request error: %v", err) - _, err = msg.ReplyText(errMsg) + _, err = h.msg.ReplyText(errMsg) if err != nil { return errors.New(fmt.Sprintf("response user error: %v ", err)) } return err } - if reply == "" { - return nil - } - // 设置上下文,回复用户 - UserService.SetUserSessionContext(sender.ID(), requestText, reply) - _, err = msg.ReplyText(buildUserReply(reply)) + // 2.设置上下文,回复用户 + h.service.SetUserSessionContext(h.msg.Content, reply) + _, err = h.msg.ReplyText(buildUserReply(reply)) if err != nil { return errors.New(fmt.Sprintf("response user error: %v ", err)) } + + // 3.返回错误 return err } +// getRequestText 获取请求接口的文本,要做一些清晰 +func (h *UserMessageHandler) getRequestText() string { + // 1.去除空格以及换行 + text := strings.TrimSpace(h.msg.Content) + text = strings.Trim(h.msg.Content, "\n") + + // 2.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算) + requestText := h.service.GetUserSessionContext() + text + if len(requestText) >= 4000 { + requestText = requestText[:4000] + } + + // 3.返回请求文本 + return requestText +} + // buildUserReply 构建用户回复 func buildUserReply(reply string) string { - reply = strings.Trim(strings.Trim(reply, "?"), "\n") + // 1.去除空格问号以及换行号,如果为空,返回一个默认值提醒用户 + textSplit := strings.Split(reply, "\n\n") + if len(textSplit) > 1 { + trimText := textSplit[0] + reply = strings.Trim(reply, trimText) + } + reply = strings.TrimSpace(reply) + + reply = strings.TrimSpace(reply) if reply == "" { return "请求得不到任何有意义的回复,请具体提出问题。" } + + // 2.如果用户有配置前缀,加上前缀 reply = config.LoadConfig().ReplyPrefix + "\n" + reply reply = strings.Trim(reply, "\n") + + // 3.返回拼接好的字符串 return reply } diff --git a/service/user.go b/service/user.go index 89e1ef9..c3ab05d 100644 --- a/service/user.go +++ b/service/user.go @@ -2,17 +2,16 @@ package service import ( "github.com/869413421/wechatbot/config" + "github.com/eatmoreapple/openwechat" "github.com/patrickmn/go-cache" - "strings" "time" - "unicode/utf8" ) // UserServiceInterface 用户业务接口 type UserServiceInterface interface { - GetUserSessionContext(userId string) string - SetUserSessionContext(userId string, question, reply string) - ClearUserSessionContext(userId string, msg string) bool + GetUserSessionContext() string + SetUserSessionContext(question, reply string) + ClearUserSessionContext() } var _ UserServiceInterface = (*UserService)(nil) @@ -21,33 +20,43 @@ var _ UserServiceInterface = (*UserService)(nil) type UserService struct { // 缓存 cache *cache.Cache + // 用户 + user *openwechat.User } -// ClearUserSessionContext 清空GTP上下文,接收文本中包含`我要问下一个问题`,并且Unicode 字符数量不超过20就清空 -func (s *UserService) ClearUserSessionContext(userId string, msg string) bool { - if strings.Contains(msg, "我要问下一个问题") && utf8.RuneCountInString(msg) < 20 { - s.cache.Delete(userId) - return true +// NewUserService 创建新的业务层 +func NewUserService(cache *cache.Cache, user *openwechat.User) UserServiceInterface { + return &UserService{ + cache: cache, + user: user, } - return false } -// NewUserService 创建新的业务层 -func NewUserService() UserServiceInterface { - return &UserService{cache: cache.New(time.Second*config.LoadConfig().SessionTimeout, time.Minute*10)} +// ClearUserSessionContext 清空GTP上下文,接收文本中包含`我要问下一个问题`,并且Unicode 字符数量不超过20就清空 +func (s *UserService) ClearUserSessionContext() { + s.cache.Delete(s.user.ID()) } // GetUserSessionContext 获取用户会话上下文文本 -func (s *UserService) GetUserSessionContext(userId string) string { - sessionContext, ok := s.cache.Get(userId) +func (s *UserService) GetUserSessionContext() string { + // 1.获取上次会话信息,如果没有直接返回空字符串 + sessionContext, ok := s.cache.Get(s.user.ID()) if !ok { return "" } - return sessionContext.(string) + + // 2.如果字符长度超过等于4000,强制清空会话(超过GPT会报错)。 + contextText := sessionContext.(string) + if len(contextText) >= 4000 { + s.cache.Delete(s.user.ID()) + } + + // 3.返回上文 + return contextText } // SetUserSessionContext 设置用户会话上下文文本,question用户提问内容,GTP回复内容 -func (s *UserService) SetUserSessionContext(userId string, question, reply string) { +func (s *UserService) SetUserSessionContext(question, reply string) { value := question + "\n" + reply - s.cache.Set(userId, value, time.Second*config.LoadConfig().SessionTimeout) + s.cache.Set(s.user.ID(), value, time.Second*config.LoadConfig().SessionTimeout) } From 1e1e3ca727cb9d7716fb3fae63ec8820123c32dc Mon Sep 17 00:00:00 2001 From: eryajf Date: Sun, 11 Dec 2022 20:10:25 +0800 Subject: [PATCH 25/37] feat: add build go binary to release --- .github/workflows/go-release.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/go-release.yml diff --git a/.github/workflows/go-release.yml b/.github/workflows/go-release.yml new file mode 100644 index 0000000..a233d5d --- /dev/null +++ b/.github/workflows/go-release.yml @@ -0,0 +1,26 @@ +name: build + +on: + release: + types: [created] # 表示在创建新的 Release 时触发 + +jobs: + build-go-binary: + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux, windows, darwin] # 需要打包的系统 + goarch: [amd64, arm64] # 需要打包的架构 + exclude: # 排除某些平台和架构 + - goarch: arm64 + goos: windows + steps: + - uses: actions/checkout@v3 + - uses: wangyoucao577/go-release-action@v1.30 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} # 一个默认的变量,用来实现往 Release 中添加文件 + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + goversion: 1.16 # 可以指定编译使用的 Golang 版本 + binary_name: "wechatbot" # 可以指定二进制文件的名称 + extra_files: LICENSE README.md config.dev.json # 需要包含的额外文件 \ No newline at end of file From a71895fb48138bf3f93bd8243894e2f4a545cd01 Mon Sep 17 00:00:00 2001 From: eryajf Date: Sun, 11 Dec 2022 20:14:16 +0800 Subject: [PATCH 26/37] feat: add build go binary to release --- .github/workflows/go-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go-release.yml b/.github/workflows/go-release.yml index a233d5d..f7fbd55 100644 --- a/.github/workflows/go-release.yml +++ b/.github/workflows/go-release.yml @@ -23,4 +23,4 @@ jobs: goarch: ${{ matrix.goarch }} goversion: 1.16 # 可以指定编译使用的 Golang 版本 binary_name: "wechatbot" # 可以指定二进制文件的名称 - extra_files: LICENSE README.md config.dev.json # 需要包含的额外文件 \ No newline at end of file + extra_files: README.md config.dev.json # 需要包含的额外文件 \ No newline at end of file From 0e36038847d268768b5b7668049967289c5bd0a1 Mon Sep 17 00:00:00 2001 From: eryajf Date: Sun, 11 Dec 2022 20:36:59 +0800 Subject: [PATCH 27/37] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E8=AF=B4?= =?UTF-8?q?=E6=98=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a03808f..ff3e05a 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,24 @@ $ tail -f -n 50 /app/run.log # 快速开始 -> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases/tag/v1.1.3) ,解压运行。 +`第一种:直接下载二进制(适合对编程不了解的同学)` + +> 非技术人员请直接下载release中的[压缩包](https://github.com/869413421/wechatbot/releases) ,请根据自己系统以及架构选择合适的压缩包,下载之后直接解压运行。 + +下载之后,在本地解压,即可看到可执行程序,与配置文件: + +``` +$ tar xf wechatbot-v0.0.2-darwin-arm64.tar.gz +$ cd wechatbot-v0.0.2-darwin-arm64 +$ cp config.dev.json # 根据情况调整配置文件内容 +$ ./wechatbot # 直接运行 + +# 如果要守护在后台运行 +$ nohup ./wechatbot &> run.log & +$ tail -f run.log +``` + +`第二种:基于源码运行(适合了解go语言编程的同学)` ```` # 获取项目 @@ -98,7 +115,7 @@ go run main.go # 编译 CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o wechatbot ./main.go # 守护进程运行 -nohup ./wechatbot > run.log & +nohup ./wechatbot &> run.log & # 查看二维码 # tail -f -n 50 run.log ```` @@ -139,4 +156,3 @@ session_clear_token: 会话清空口令,默认`下一个问题` - From 6e6fdb11d505f7ba8a98fe9de33980a725f4d1ed Mon Sep 17 00:00:00 2001 From: huangyanming Date: Mon, 12 Dec 2022 10:24:57 +0800 Subject: [PATCH 28/37] =?UTF-8?q?=E4=BF=AE=E6=94=B9README=E5=92=8CGPT?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 28 ++++++++++++---------------- config/config.go | 2 +- gtp/gtp.go => gpt/gpt.go | 8 ++++---- handlers/group_msg_handler.go | 6 +++--- handlers/user_msg_handler.go | 6 +++--- 5 files changed, 23 insertions(+), 27 deletions(-) rename gtp/gtp.go => gpt/gpt.go (93%) diff --git a/README.md b/README.md index ff3e05a..198bf4f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ * GPT机器人模型热度可配置 * 提问增加上下文 -* 指令清空上下文(指令:我要问下一个问题) +* 指令清空上下文(指令:根据配置) * 机器人群聊@回复 * 机器人私聊回复 * 私聊回复前缀设置 @@ -86,6 +86,13 @@ $ tail -f -n 50 /app/run.log 下载之后,在本地解压,即可看到可执行程序,与配置文件: ``` +# windows +1.下载exe +2.复制代码中config.dev.json更改为config.json +3.将config.json中的api_key替换为自己的 +4.双击exe,扫码登录 + +# linux $ tar xf wechatbot-v0.0.2-darwin-arm64.tar.gz $ cd wechatbot-v0.0.2-darwin-arm64 $ cp config.dev.json # 根据情况调整配置文件内容 @@ -100,24 +107,16 @@ $ tail -f run.log ```` # 获取项目 -git clone https://github.com/869413421/wechatbot.git +$ git clone https://github.com/869413421/wechatbot.git # 进入项目目录 -cd wechatbot +$ cd wechatbot # 复制配置文件 -copy config.dev.json config.json +$ copy config.dev.json config.json # 启动项目 -go run main.go - -# linux编译,守护进程运行(可选) -# 编译 -CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-w' -o wechatbot ./main.go -# 守护进程运行 -nohup ./wechatbot &> run.log & -# 查看二维码 -# tail -f -n 50 run.log +$ go run main.go ```` # 配置文件说明 @@ -145,9 +144,6 @@ session_clear_token: 会话清空口令,默认`下一个问题` ```` # 使用示例 - -### 向机器人发送`下一个问题`,清空会话信息。 - ### 私聊 diff --git a/config/config.go b/config/config.go index c4f6d2e..24f4faa 100644 --- a/config/config.go +++ b/config/config.go @@ -13,7 +13,7 @@ import ( // Configuration 项目配置 type Configuration struct { - // gtp apikey + // gpt apikey ApiKey string `json:"api_key"` // 自动通过好友 AutoPass bool `json:"auto_pass"` diff --git a/gtp/gtp.go b/gpt/gpt.go similarity index 93% rename from gtp/gtp.go rename to gpt/gpt.go index f1351ae..b99feb8 100644 --- a/gtp/gtp.go +++ b/gpt/gpt.go @@ -1,4 +1,4 @@ -package gtp +package gpt import ( "bytes" @@ -64,7 +64,7 @@ func Completions(msg string) (string, error) { if err != nil { return "", err } - logger.Info(fmt.Sprintf("request gtp json string : %v", string(requestData))) + logger.Info(fmt.Sprintf("request gpt json string : %v", string(requestData))) req, err := http.NewRequest("POST", BASEURL+"completions", bytes.NewBuffer(requestData)) if err != nil { return "", err @@ -81,13 +81,13 @@ func Completions(msg string) (string, error) { defer response.Body.Close() if response.StatusCode != 200 { body, _ := ioutil.ReadAll(response.Body) - return "", errors.New(fmt.Sprintf("请求GTP出错了,gtp api status code not equals 200,code is %d ,details: %v ", response.StatusCode, string(body))) + return "", errors.New(fmt.Sprintf("请求GTP出错了,gpt api status code not equals 200,code is %d ,details: %v ", response.StatusCode, string(body))) } body, err := ioutil.ReadAll(response.Body) if err != nil { return "", err } - logger.Info(fmt.Sprintf("response gtp json string : %v", string(body))) + logger.Info(fmt.Sprintf("response gpt json string : %v", string(body))) gptResponseBody := &ChatGPTResponseBody{} log.Println(string(body)) diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 9fec613..0891ce6 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -3,7 +3,7 @@ package handlers import ( "errors" "fmt" - "github.com/869413421/wechatbot/gtp" + "github.com/869413421/wechatbot/gpt" "github.com/869413421/wechatbot/pkg/logger" "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" @@ -74,10 +74,10 @@ func (g *GroupMessageHandler) ReplyText() error { } // 3.请求GPT获取回复 - reply, err := gtp.Completions(requestText) + reply, err := gpt.Completions(requestText) if err != nil { // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 - errMsg := fmt.Sprintf("gtp request error: %v", err) + errMsg := fmt.Sprintf("gpt request error: %v", err) _, err = g.msg.ReplyText(errMsg) if err != nil { return errors.New(fmt.Sprintf("response group error: %v ", err)) diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 3190829..3c0edc4 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" "github.com/869413421/wechatbot/config" - "github.com/869413421/wechatbot/gtp" + "github.com/869413421/wechatbot/gpt" "github.com/869413421/wechatbot/pkg/logger" "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" @@ -58,10 +58,10 @@ func (h *UserMessageHandler) ReplyText() error { } // 2.向GPT发起请求,如果回复文本等于空,不回复 - reply, err := gtp.Completions(h.getRequestText()) + reply, err := gpt.Completions(h.getRequestText()) if err != nil { // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 - errMsg := fmt.Sprintf("gtp request error: %v", err) + errMsg := fmt.Sprintf("gpt request error: %v", err) _, err = h.msg.ReplyText(errMsg) if err != nil { return errors.New(fmt.Sprintf("response user error: %v ", err)) From 6e56c52d20405375305c4a633ba86802b5df4ca2 Mon Sep 17 00:00:00 2001 From: "543132969@qq.com" Date: Mon, 12 Dec 2022 16:46:55 +0800 Subject: [PATCH 29/37] =?UTF-8?q?=E4=BD=BF=E7=94=A8openwechat=20dispatcher?= =?UTF-8?q?=E4=BC=98=E5=8C=96hanlers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bootstrap/bootstrap.go | 10 ++++-- handlers/group_msg_handler.go | 18 ++++++++++ handlers/handler.go | 67 +++++++++-------------------------- handlers/token_msg_handler.go | 19 ++++++++++ handlers/user_msg_handler.go | 16 +++++++++ 5 files changed, 77 insertions(+), 53 deletions(-) diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 22c3e5f..8d3c435 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -7,13 +7,17 @@ import ( "github.com/eatmoreapple/openwechat" ) - func Run() { //bot := openwechat.DefaultBot() bot := openwechat.DefaultBot(openwechat.Desktop) // 桌面模式,上面登录不上的可以尝试切换这种模式 // 注册消息处理函数 - bot.MessageHandler = handlers.Handler + handler, err := handlers.NewHandler() + if err != nil { + logger.Danger("register error: %v", err) + return + } + bot.MessageHandler = handler // 注册登陆二维码回调 bot.UUIDCallback = handlers.QrCodeCallBack @@ -22,7 +26,7 @@ func Run() { reloadStorage := openwechat.NewJsonFileHotReloadStorage("storage.json") // 执行热登录 - err := bot.HotLogin(reloadStorage,true) + err = bot.HotLogin(reloadStorage, true) if err != nil { logger.Warning(fmt.Sprintf("login error: %v ", err)) return diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 0891ce6..6a1f547 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -26,6 +26,24 @@ type GroupMessageHandler struct { service service.UserServiceInterface } +func GroupMessageContextHandler() func(ctx *openwechat.MessageContext) { + return func(ctx *openwechat.MessageContext) { + msg := ctx.Message + // 获取用户消息处理器 + handler, err := NewGroupMessageHandler(msg) + if err != nil { + logger.Warning(fmt.Sprintf("init group message handler error: %s", err)) + return + } + + // 处理用户消息 + err = handler.handle() + if err != nil { + logger.Warning(fmt.Sprintf("handle group message error: %s", err)) + } + } +} + // NewGroupMessageHandler 创建群消息处理器 func NewGroupMessageHandler(msg *openwechat.Message) (MessageHandlerInterface, error) { sender, err := msg.Sender() diff --git a/handlers/handler.go b/handlers/handler.go index 6d825ad..1ce2992 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -35,50 +35,24 @@ func QrCodeCallBack(uuid string) { } } -// Handler 全局处理入口 -func Handler(msg *openwechat.Message) { - defer func() { - err := recover() - if err != nil { - logger.Warning(fmt.Sprintf("handler recover error: %v", err)) - } - }() +func NewHandler() (msgFunc func(msg *openwechat.Message), err error) { + dispatcher := openwechat.NewMessageMatchDispatcher() // 清空会话 - if strings.Contains(msg.Content, config.LoadConfig().SessionClearToken) { - // 获取口令消息处理器 - handler, err := NewTokenMessageHandler(msg) - if err != nil { - logger.Warning(fmt.Sprintf("init token message handler error: %s", err)) - } - - // 获取口令消息处理器 - err = handler.handle() - if err != nil { - logger.Warning(fmt.Sprintf("handle token message error: %s", err)) - } - return - } + dispatcher.RegisterHandler(func(message *openwechat.Message) bool { + return strings.Contains(message.Content, config.LoadConfig().SessionClearToken) + }, TokenMessageContextHandler()) // 处理群消息 - if msg.IsSendByGroup() { - // 获取用户消息处理器 - handler, err := NewGroupMessageHandler(msg) - if err != nil { - logger.Warning(fmt.Sprintf("init group message handler error: %s", err)) - return - } - - // 处理用户消息 - err = handler.handle() - if err != nil { - logger.Warning(fmt.Sprintf("handle group message error: %s", err)) - } - return - } + dispatcher.RegisterHandler(func(message *openwechat.Message) bool { + return message.IsSendByGroup() + }, GroupMessageContextHandler()) // 好友申请 - if msg.IsFriendAdd() { + dispatcher.RegisterHandler(func(message *openwechat.Message) bool { + return message.IsFriendAdd() + }, func(ctx *openwechat.MessageContext) { + msg := ctx.Message if config.LoadConfig().AutoPass { _, err := msg.Agree("") if err != nil { @@ -86,19 +60,12 @@ func Handler(msg *openwechat.Message) { return } } - } + }) // 私聊 // 获取用户消息处理器 - handler, err := NewUserMessageHandler(msg) - if err != nil { - logger.Warning(fmt.Sprintf("init user message handler error: %s", err)) - } - - // 处理用户消息 - err = handler.handle() - if err != nil { - logger.Warning(fmt.Sprintf("handle user message error: %s", err)) - } - return + dispatcher.RegisterHandler(func(message *openwechat.Message) bool { + return !(strings.Contains(message.Content, config.LoadConfig().SessionClearToken) || message.IsSendByGroup() || message.IsFriendAdd()) + }, UserMessageContextHandler()) + return openwechat.DispatchMessage(dispatcher), nil } diff --git a/handlers/token_msg_handler.go b/handlers/token_msg_handler.go index 7a75111..669be03 100644 --- a/handlers/token_msg_handler.go +++ b/handlers/token_msg_handler.go @@ -1,6 +1,7 @@ package handlers import ( + "fmt" "github.com/869413421/wechatbot/pkg/logger" "github.com/869413421/wechatbot/service" "github.com/eatmoreapple/openwechat" @@ -18,6 +19,24 @@ type TokenMessageHandler struct { service service.UserServiceInterface } +func TokenMessageContextHandler() func(ctx *openwechat.MessageContext) { + return func(ctx *openwechat.MessageContext) { + msg := ctx.Message + // 获取口令消息处理器 + handler, err := NewTokenMessageHandler(msg) + if err != nil { + logger.Warning(fmt.Sprintf("init token message handler error: %s", err)) + } + + // 获取口令消息处理器 + err = handler.handle() + if err != nil { + logger.Warning(fmt.Sprintf("handle token message error: %s", err)) + } + + } +} + // NewTokenMessageHandler 口令消息处理器 func NewTokenMessageHandler(msg *openwechat.Message) (MessageHandlerInterface, error) { sender, err := msg.Sender() diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 3c0edc4..b6aa16a 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -23,6 +23,22 @@ type UserMessageHandler struct { service service.UserServiceInterface } +func UserMessageContextHandler() func(ctx *openwechat.MessageContext) { + return func(ctx *openwechat.MessageContext) { + msg := ctx.Message + handler, err := NewUserMessageHandler(msg) + if err != nil { + logger.Warning(fmt.Sprintf("init user message handler error: %s", err)) + } + + // 处理用户消息 + err = handler.handle() + if err != nil { + logger.Warning(fmt.Sprintf("handle user message error: %s", err)) + } + } +} + // NewUserMessageHandler 创建私聊处理器 func NewUserMessageHandler(message *openwechat.Message) (MessageHandlerInterface, error) { sender, err := message.Sender() From b908859bcba3e505e7e3d4d37e14dbf2959872e5 Mon Sep 17 00:00:00 2001 From: 869413421 <13528685024@163.com> Date: Mon, 12 Dec 2022 23:04:12 +0800 Subject: [PATCH 30/37] =?UTF-8?q?1.=E7=BE=A4=E8=81=8A=E9=9C=80=E8=A6=81@?= =?UTF-8?q?=E6=89=8D=E8=83=BD=E6=B8=85=E7=A9=BA=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=202.=E4=BC=98=E5=8C=96=E4=B8=8A=E4=B8=8B=E6=96=87=EF=BC=8C?= =?UTF-8?q?=E6=8F=90=E9=AB=98GPT=E5=9B=9E=E5=A4=8D=E6=95=88=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 21 +++++++++++++++------ handlers/group_msg_handler.go | 21 +++++++++++++++------ handlers/token_msg_handler.go | 3 +++ handlers/user_msg_handler.go | 23 +++++++++++++++++------ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 198bf4f..68a34d0 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ * 其他无法登录问题,依然尝试删除掉storage.json文件重新登录。 * ~~机器人无法正常回复,检查ApiKey能否正常使用,控制台日志中有详细错误信息~~ 新版本会机器人会直接输出,因为被问得好烦了。 * linux中二维码无法扫描,缩小命令行功能,让二维码像素尽可能清晰。(无法从代码层面解决) -* 机器人一直答非所问,可能因为上下文累积过多。切换不同问题时,发送指令:我要问下一个问题。会清空上下文 +* 机器人一直答非所问,可能因为上下文累积过多。切换不同问题时,发送指令:启动时配置的`session_clear_token`字段。会清空上下文 # 使用前提 @@ -52,7 +52,16 @@ ```sh # 运行项目,环境变量参考下方配置说明 -$ docker run -itd --name wechatbot --restart=always -e APIKEY=换成你的key -e AUTO_PASS=false -e SESSION_TIMEOUT=60s -e MODEL=text-davinci-003 -e MAX_TOKENS=512 -e TEMPREATURE=0.9 -e REPLY_PREFIX=我是来自机器人回复: -e SESSION_CLEAR_TOKEN=下一个问题 docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +$ docker run -itd --name wechatbot --restart=always \ + -e APIKEY=换成你的key \ + -e AUTO_PASS=false \ + -e SESSION_TIMEOUT=60s \ + -e MODEL=text-davinci-003 \ + -e MAX_TOKENS=512 \ + -e TEMPREATURE=0.9 \ + -e REPLY_PREFIX=我是来自机器人回复: \ + -e SESSION_CLEAR_TOKEN=下一个问题 \ + docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 $ docker exec -it wechatbot bash @@ -65,10 +74,10 @@ $ tail -f -n 50 /app/run.log ```sh # 复制配置文件,根据自己实际情况,调整配置里的内容 -cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录获取 +$ cp config.dev.json config.json # 其中 config.dev.json 从项目的根目录获取 # 运行项目 -docker run -itd --name wechatbot -v `pwd`/config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest +$ docker run -itd --name wechatbot -v `pwd`/config.json:/app/config.json docker.mirrors.sjtug.sjtu.edu.cn/qingshui869413421/wechatbot:latest # 查看二维码 $ docker exec -it wechatbot bash @@ -87,8 +96,8 @@ $ tail -f -n 50 /app/run.log ``` # windows -1.下载exe -2.复制代码中config.dev.json更改为config.json +1.下载压缩包解压 +2.复制文件中config.dev.json更改为config.json 3.将config.json中的api_key替换为自己的 4.双击exe,扫码登录 diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 0891ce6..4f4edf1 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -86,9 +86,7 @@ func (g *GroupMessageHandler) ReplyText() error { } // 4.设置上下文,并响应信息给用户 - selfName := "@" + g.self.NickName - question := strings.ReplaceAll(g.msg.Content, selfName, "") - g.service.SetUserSessionContext(question, reply) + g.service.SetUserSessionContext(requestText, reply) _, err = g.msg.ReplyText(g.buildReplyText(reply)) if err != nil { return errors.New(fmt.Sprintf("response user error: %v ", err)) @@ -111,13 +109,24 @@ func (g *GroupMessageHandler) getRequestText() string { return "" } - // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算) - requestText = g.service.GetUserSessionContext() + requestText + // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算),达芬奇3最大为4068,也许后续为了适应要动态进行判断。 + sessionText := g.service.GetUserSessionContext() + if sessionText != "" { + requestText = sessionText + "\n" + requestText + } if len(requestText) >= 4000 { requestText = requestText[:4000] } - // 4.返回请求文本 + // 4.检查用户发送文本是否包含结束标点符号 + punctuation := ",.;!?,。!?、…" + runeRequestText := []rune(requestText) + lastChar := string(runeRequestText[len(runeRequestText)-1:]) + if strings.Index(punctuation, lastChar) < 0 { + requestText = requestText + "?" // 判断最后字符是否加了标点,没有的话加上句号,避免openai自动补齐引起混乱。 + } + + // 5.返回请求文本 return requestText } diff --git a/handlers/token_msg_handler.go b/handlers/token_msg_handler.go index 7a75111..c4c72aa 100644 --- a/handlers/token_msg_handler.go +++ b/handlers/token_msg_handler.go @@ -48,6 +48,9 @@ func (t *TokenMessageHandler) ReplyText() error { t.service.ClearUserSessionContext() var err error if t.msg.IsComeFromGroup() { + if !t.msg.IsAt() { + return err + } atText := "@" + t.sender.NickName + "上下文已经清空,请问下一个问题。" _, err = t.msg.ReplyText(atText) } else { diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 3c0edc4..cbf141e 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -70,7 +70,7 @@ func (h *UserMessageHandler) ReplyText() error { } // 2.设置上下文,回复用户 - h.service.SetUserSessionContext(h.msg.Content, reply) + h.service.SetUserSessionContext(requestText, reply) _, err = h.msg.ReplyText(buildUserReply(reply)) if err != nil { return errors.New(fmt.Sprintf("response user error: %v ", err)) @@ -83,16 +83,27 @@ func (h *UserMessageHandler) ReplyText() error { // getRequestText 获取请求接口的文本,要做一些清晰 func (h *UserMessageHandler) getRequestText() string { // 1.去除空格以及换行 - text := strings.TrimSpace(h.msg.Content) - text = strings.Trim(h.msg.Content, "\n") + requestText := strings.TrimSpace(h.msg.Content) + requestText = strings.Trim(h.msg.Content, "\n") - // 2.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算) - requestText := h.service.GetUserSessionContext() + text + // 2.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算),达芬奇3最大为4068,也许后续为了适应要动态进行判断。 + sessionText := h.service.GetUserSessionContext() + if sessionText != "" { + requestText = sessionText + "\n" + requestText + } if len(requestText) >= 4000 { requestText = requestText[:4000] } - // 3.返回请求文本 + // 3.检查用户发送文本是否包含结束标点符号 + punctuation := ",.;!?,。!?、…" + runeRequestText := []rune(requestText) + lastChar := string(runeRequestText[len(runeRequestText)-1:]) + if strings.Index(punctuation, lastChar) < 0 { + requestText = requestText + "?" // 判断最后字符是否加了标点,没有的话加上句号,避免openai自动补齐引起混乱。 + } + + // 4.返回请求文本 return requestText } From 1b94ec069eaa8ca544b7bd17d71ff64fc8f90adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=A0=E5=B1=B1?= <290794272@qq.com> Date: Tue, 13 Dec 2022 10:47:54 +0800 Subject: [PATCH 31/37] =?UTF-8?q?=E7=BC=96=E5=86=99Readme=E6=96=87?= =?UTF-8?q?=E6=A1=A3=EF=BC=9A=201.301=E6=8A=A5=E9=94=99=E8=A7=A3=E5=86=B3?= =?UTF-8?q?=E6=96=B9=E6=A1=88=202.docker=E9=83=A8=E7=BD=B2=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=E5=88=A0=E9=99=A4storage.json=E9=87=8D=E5=90=AF?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68a34d0..fa240f4 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ # 常见问题 * 如无法登录 login error: write storage.json: bad file descriptor 删除掉storage.json文件重新登录。 -* 其他无法登录问题,依然尝试删除掉storage.json文件重新登录。 +* 如无法登录 login error: wechat network error: Get "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage": 301 response missing Location header 一般是微信登录权限问题,先确保PC端能否正常登录。 +* 其他无法登录问题,依然尝试删除掉storage.json文件,结束进程(linux一般是kill -9 进程id)之后重启程序,重新扫码登录,(如为docket部署,Supervisord进程管理工具会自动重启程序)。 * ~~机器人无法正常回复,检查ApiKey能否正常使用,控制台日志中有详细错误信息~~ 新版本会机器人会直接输出,因为被问得好烦了。 * linux中二维码无法扫描,缩小命令行功能,让二维码像素尽可能清晰。(无法从代码层面解决) * 机器人一直答非所问,可能因为上下文累积过多。切换不同问题时,发送指令:启动时配置的`session_clear_token`字段。会清空上下文 From 7a48ad17ad900269ab81da87cc79ae4ad198dc5c Mon Sep 17 00:00:00 2001 From: qingconglaixueit <502892037@qq.com> Date: Wed, 14 Dec 2022 10:28:44 +0800 Subject: [PATCH 32/37] [add] add code --- bootstrap/bootstrap.go | 4 ++-- config/config.go | 2 +- go.mod | 2 +- gpt/gpt.go | 4 ++-- handlers/group_msg_handler.go | 6 +++--- handlers/handler.go | 4 ++-- handlers/token_msg_handler.go | 4 ++-- handlers/user_msg_handler.go | 8 ++++---- main.go | 2 +- service/user.go | 2 +- 10 files changed, 19 insertions(+), 19 deletions(-) diff --git a/bootstrap/bootstrap.go b/bootstrap/bootstrap.go index 8d3c435..7a3166f 100644 --- a/bootstrap/bootstrap.go +++ b/bootstrap/bootstrap.go @@ -2,8 +2,8 @@ package bootstrap import ( "fmt" - "github.com/869413421/wechatbot/handlers" - "github.com/869413421/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/handlers" + "github.com/qingconglaixueit/wechatbot/pkg/logger" "github.com/eatmoreapple/openwechat" ) diff --git a/config/config.go b/config/config.go index 24f4faa..d0a7298 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,7 @@ package config import ( "encoding/json" "fmt" - "github.com/869413421/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/pkg/logger" "log" "os" "strconv" diff --git a/go.mod b/go.mod index 3d6f011..bb1bedb 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/869413421/wechatbot +module github.com/qingconglaixueit/wechatbot go 1.16 diff --git a/gpt/gpt.go b/gpt/gpt.go index b99feb8..ec6c39b 100644 --- a/gpt/gpt.go +++ b/gpt/gpt.go @@ -5,8 +5,8 @@ import ( "encoding/json" "errors" "fmt" - "github.com/869413421/wechatbot/config" - "github.com/869413421/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/config" + "github.com/qingconglaixueit/wechatbot/pkg/logger" "io/ioutil" "log" "net/http" diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 4b04154..0ad6c43 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -3,9 +3,9 @@ package handlers import ( "errors" "fmt" - "github.com/869413421/wechatbot/gpt" - "github.com/869413421/wechatbot/pkg/logger" - "github.com/869413421/wechatbot/service" + "github.com/qingconglaixueit/wechatbot/gpt" + "github.com/qingconglaixueit/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/service" "github.com/eatmoreapple/openwechat" "strings" ) diff --git a/handlers/handler.go b/handlers/handler.go index 1ce2992..7a608df 100644 --- a/handlers/handler.go +++ b/handlers/handler.go @@ -2,8 +2,8 @@ package handlers import ( "fmt" - "github.com/869413421/wechatbot/config" - "github.com/869413421/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/config" + "github.com/qingconglaixueit/wechatbot/pkg/logger" "github.com/eatmoreapple/openwechat" "github.com/patrickmn/go-cache" "github.com/skip2/go-qrcode" diff --git a/handlers/token_msg_handler.go b/handlers/token_msg_handler.go index 8ac0f60..13393c3 100644 --- a/handlers/token_msg_handler.go +++ b/handlers/token_msg_handler.go @@ -2,8 +2,8 @@ package handlers import ( "fmt" - "github.com/869413421/wechatbot/pkg/logger" - "github.com/869413421/wechatbot/service" + "github.com/qingconglaixueit/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/service" "github.com/eatmoreapple/openwechat" ) diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index e163ae8..17fc947 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -3,10 +3,10 @@ package handlers import ( "errors" "fmt" - "github.com/869413421/wechatbot/config" - "github.com/869413421/wechatbot/gpt" - "github.com/869413421/wechatbot/pkg/logger" - "github.com/869413421/wechatbot/service" + "github.com/qingconglaixueit/wechatbot/config" + "github.com/qingconglaixueit/wechatbot/gpt" + "github.com/qingconglaixueit/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/service" "github.com/eatmoreapple/openwechat" "strings" ) diff --git a/main.go b/main.go index 0e18cf1..17d0fbc 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/869413421/wechatbot/bootstrap" + "github.com/qingconglaixueit/wechatbot/bootstrap" ) func main() { diff --git a/service/user.go b/service/user.go index c3ab05d..a128aad 100644 --- a/service/user.go +++ b/service/user.go @@ -1,7 +1,7 @@ package service import ( - "github.com/869413421/wechatbot/config" + "github.com/qingconglaixueit/wechatbot/config" "github.com/eatmoreapple/openwechat" "github.com/patrickmn/go-cache" "time" From d449af6a52e7430f4a99bebac052e097cfc1f4f9 Mon Sep 17 00:00:00 2001 From: qingconglaixueit <502892037@qq.com> Date: Sat, 7 Jan 2023 16:00:56 +0800 Subject: [PATCH 33/37] [add]add code , add chatgpt service time and vip user --- README.md | 4 +- config/config.go | 27 ++++++++++ handlers/group_msg_handler.go | 94 ++++++++++++++++++++++++++++++----- handlers/user_msg_handler.go | 30 +++++++---- pkg/logger/logger.go | 2 +- rule/rule.go | 56 +++++++++++++++++++++ 6 files changed, 189 insertions(+), 24 deletions(-) create mode 100644 rule/rule.go diff --git a/README.md b/README.md index fa240f4..f739960 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # wechatbot +> 本项目是 fork 他人的项目来进行学习和使用,请问商用,可以下载下来做自定义的功能 > 最近ChatGPT异常火爆,本项目可以将个人微信化身GPT机器人, > 项目基于[openwechat](https://github.com/eatmoreapple/openwechat) 开发。 @@ -18,11 +19,12 @@ * 机器人私聊回复 * 私聊回复前缀设置 * 好友添加自动通过 +* 增加每天工作的起始时间和结束时间,只有在该时间段才会对外提供 chatgpt 服务 # 实现机制 目前机器人有两种实现方式 * 逆向功能,扒取官网API,通过抓取cookie获取GPT响应信息,`优点:`效果与官网一致,`缺点:`cookie会过期需要不定时更新。 -* 基于openai官网提供的API,`优点`:模型以及各种参数可以自由配置,`缺点:`效果达不到官网智能,且API收费,新账号有18元免费额度。 +* 基于openai官网提供的API,`优点`:模型以及各种参数可以自由配置,`缺点:`效果达不到官网智能,且API收费,新账号有18美元免费额度。 > 本项目基于第二种方式实现,模型之间具体差异可以参考[官方文档](https://beta.openai.com/docs/models/overview), 详细[参数示例](https://beta.openai.com/examples) 。 diff --git a/config/config.go b/config/config.go index d0a7298..5c3a123 100644 --- a/config/config.go +++ b/config/config.go @@ -29,6 +29,10 @@ type Configuration struct { ReplyPrefix string `json:"reply_prefix"` // 清空会话口令 SessionClearToken string `json:"session_clear_token"` + // 每天工作起始时间 + StartTime int `json:"start_time"` + // 每天工作结束时间 + EndTime int `json:"end_time"` } var config *Configuration @@ -45,6 +49,8 @@ func LoadConfig() *Configuration { Model: "text-davinci-003", Temperature: 0.9, SessionClearToken: "下一个问题", + StartTime: 9, + EndTime: 21, } // 判断配置文件是否存在,存在直接JSON读取 @@ -72,6 +78,8 @@ func LoadConfig() *Configuration { Temperature := os.Getenv("TEMPREATURE") ReplyPrefix := os.Getenv("REPLY_PREFIX") SessionClearToken := os.Getenv("SESSION_CLEAR_TOKEN") + StartTime := os.Getenv("START_TIME") + EndTime := os.Getenv("END_TIME") if ApiKey != "" { config.ApiKey = ApiKey } @@ -111,6 +119,25 @@ func LoadConfig() *Configuration { if SessionClearToken != "" { config.SessionClearToken = SessionClearToken } + + if StartTime != "" { + sTime, err := strconv.Atoi(StartTime) + if err != nil { + logger.Warning("StartTime=%s strconv.Atoi error:%+v",StartTime,err) + }else{ + config.StartTime = sTime + } + } + + if EndTime != "" { + eTime, err := strconv.Atoi(EndTime) + if err != nil { + logger.Warning("EndTime=%s strconv.Atoi error:%+v",EndTime,err) + }else{ + config.EndTime = eTime + } + } + }) if config.ApiKey == "" { logger.Danger("config err: api key required") diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 0ad6c43..734cdcd 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -3,13 +3,31 @@ package handlers import ( "errors" "fmt" + "github.com/eatmoreapple/openwechat" + "github.com/qingconglaixueit/wechatbot/config" "github.com/qingconglaixueit/wechatbot/gpt" "github.com/qingconglaixueit/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/rule" "github.com/qingconglaixueit/wechatbot/service" - "github.com/eatmoreapple/openwechat" "strings" ) +const ( + HaveARest = "该休息了" + WorkStr = "起来嗨" + WorkTime = "你的工作时间" + + replyForRest = "好的,我开始睡美容觉了!" + replyForWork = "主人,我现在活力满满!" + + replyForRest2 = "我已经休息了,你也快睡觉吧!" + + replyPersonal = "不要单独聊我哦,可以到群里互动哦!!" + + replyWorkTime = "我的工作时间是 %d 点 -- %d 点" +) + +var VipUserList = []string{"xxx", "xxx"} var _ MessageHandlerInterface = (*GroupMessageHandler)(nil) // GroupMessageHandler 群消息处理 @@ -76,9 +94,14 @@ func (g *GroupMessageHandler) handle() error { return nil } -// ReplyText 发送文本消息到群 +// ReplyText 发息送文本消到群 func (g *GroupMessageHandler) ReplyText() error { logger.Info(fmt.Sprintf("Received Group %v Text Msg : %v", g.group.NickName, g.msg.Content)) + var ( + err error + reply string + ) + // 1.不是@的不处理 if !g.msg.IsAt() { return nil @@ -90,17 +113,53 @@ func (g *GroupMessageHandler) ReplyText() error { logger.Info("user message is null") return nil } + logger.Info(fmt.Sprintf("fromname:%s requestText == %+v", g.sender.NickName, requestText)) - // 3.请求GPT获取回复 - reply, err := gpt.Completions(requestText) - if err != nil { - // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 - errMsg := fmt.Sprintf("gpt request error: %v", err) - _, err = g.msg.ReplyText(errMsg) - if err != nil { - return errors.New(fmt.Sprintf("response group error: %v ", err)) + if requestText == WorkTime { + reply = fmt.Sprintf(replyWorkTime, config.LoadConfig().StartTime, config.LoadConfig().EndTime) + } + + // 识别到是 Anonymous 发送过来的消息,且是 “该休息了!”,那么则将全局变量设置为 false,休息状态 + if rule.Grule.InSlice(g.sender.NickName, VipUserList) && requestText == HaveARest { + rule.Grule.SetWork(false) + reply = replyForRest + } + if rule.Grule.InSlice(g.sender.NickName, VipUserList) && requestText == WorkStr { + rule.Grule.SetWork(true) + reply = replyForWork + } + + if rule.Grule.GetWork() && reply == "" { + isSvr := true + if !rule.Grule.IsWorkTime(config.LoadConfig().StartTime, config.LoadConfig().EndTime) { + isSvr = false + reply = replyForRest2 + } + // 非工作时间,仍然服务 vip 用户 + if !rule.Grule.IsWorkTime(config.LoadConfig().StartTime, config.LoadConfig().EndTime) && + rule.Grule.InSlice(g.sender.NickName, VipUserList) { + isSvr = true + reply = "" + } + // 能服务的时候,才请求 chatgpt + if isSvr { + // 3.请求GPT获取回复 + //reply = "干你哦!!!" + reply, err = gpt.Completions(requestText) + if err != nil { + // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 + errMsg := fmt.Sprintf("gpt request error: %v", err) + _, err = g.msg.ReplyText(errMsg) + if err != nil { + return errors.New(fmt.Sprintf("response group error: %v ", err)) + } + return err + } + } + } else { + if reply == "" { + reply = replyForRest2 } - return err } // 4.设置上下文,并响应信息给用户 @@ -120,6 +179,8 @@ func (g *GroupMessageHandler) getRequestText() string { requestText := strings.TrimSpace(g.msg.Content) requestText = strings.Trim(g.msg.Content, "\n") + logger.Info(fmt.Sprintf("xxxxxxxxxxxxxxxx -- fromname:%s requestText == %+v", g.sender.NickName, requestText)) + // 2.替换掉当前用户名称 replaceText := "@" + g.self.NickName requestText = strings.TrimSpace(strings.ReplaceAll(g.msg.Content, replaceText, "")) @@ -127,7 +188,16 @@ func (g *GroupMessageHandler) getRequestText() string { return "" } - // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算),达芬奇3最大为4068,也许后续为了适应要动态进行判断。 + logger.Info(fmt.Sprintf("xxxxxxxxxxxxxxxx22 -- fromname:%s requestText==%+v", g.sender.NickName, requestText)) + + if requestText == WorkTime { + return requestText + } + if (requestText == WorkStr || requestText == HaveARest) && rule.Grule.InSlice(g.sender.NickName, VipUserList){ + return requestText + } + + // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算),达芬奇3最大为4068,也许后续为了适应要动态进行判断。 sessionText := g.service.GetUserSessionContext() if sessionText != "" { requestText = sessionText + "\n" + requestText diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 17fc947..f8bc514 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -3,11 +3,12 @@ package handlers import ( "errors" "fmt" + "github.com/eatmoreapple/openwechat" "github.com/qingconglaixueit/wechatbot/config" "github.com/qingconglaixueit/wechatbot/gpt" "github.com/qingconglaixueit/wechatbot/pkg/logger" + "github.com/qingconglaixueit/wechatbot/rule" "github.com/qingconglaixueit/wechatbot/service" - "github.com/eatmoreapple/openwechat" "strings" ) @@ -66,23 +67,32 @@ func (h *UserMessageHandler) handle() error { // ReplyText 发送文本消息到群 func (h *UserMessageHandler) ReplyText() error { logger.Info(fmt.Sprintf("Received User %v Text Msg : %v", h.sender.NickName, h.msg.Content)) + var ( + reply string + err error + ) // 1.获取上下文,如果字符串为空不处理 requestText := h.getRequestText() if requestText == "" { logger.Info("user message is null") return nil } - - // 2.向GPT发起请求,如果回复文本等于空,不回复 - reply, err := gpt.Completions(h.getRequestText()) - if err != nil { - // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 - errMsg := fmt.Sprintf("gpt request error: %v", err) - _, err = h.msg.ReplyText(errMsg) + logger.Info(fmt.Sprintf("h.sender.NickName == %+v", h.sender.NickName)) + // 只有 vip 用户才能私聊 + if rule.Grule.InSlice(h.sender.NickName, VipUserList) { + // 2.向GPT发起请求,如果回复文本等于空,不回复 + reply, err = gpt.Completions(h.getRequestText()) if err != nil { - return errors.New(fmt.Sprintf("response user error: %v ", err)) + // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 + errMsg := fmt.Sprintf("gpt request error: %v", err) + _, err = h.msg.ReplyText(errMsg) + if err != nil { + return errors.New(fmt.Sprintf("response user error: %v ", err)) + } + return err } - return err + } else { + reply = replyPersonal } // 2.设置上下文,回复用户 diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 7a3955d..bdc0b4b 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -11,7 +11,7 @@ var once sync.Once func init() { once.Do(func() { - Logger = log.New(os.Stdout, "INFO", log.Ldate|log.Ltime|log.Lshortfile) + Logger = log.New(os.Stdout, "INFO", log.LstdFlags|log.Llongfile) }) } diff --git a/rule/rule.go b/rule/rule.go new file mode 100644 index 0000000..f9f3608 --- /dev/null +++ b/rule/rule.go @@ -0,0 +1,56 @@ +package rule + +import ( + "sync" + "time" +) + +const ( + STARTTIME = 9 + ENDTIME = 21 +) + +type Rule struct{} + +var isWork = true +var Grule = &Rule{} +var lock sync.Mutex + +func (r *Rule) SetWork(work bool) { + lock.Lock() + defer lock.Unlock() + isWork = work + return +} +func (r *Rule) GetWork() bool { + lock.Lock() + defer lock.Unlock() + return isWork +} + +// 判断时间在今天的早上 9点到 晚上 9 点区间内 +func (r *Rule) IsWorkTime(s int, e int) bool { + if s < 0 || s > 24 { + s = STARTTIME + } + if e < 0 || e > 24 || e <= s { + e = ENDTIME + } + t := time.Now() + startTime := time.Date(t.Year(), t.Month(), t.Day(), s, 0, 0, 0, time.Local) + endTime := time.Date(t.Year(), t.Month(), t.Day(), e, 0, 0, 0, time.Local) + // 判断当前时间是否在当天的 STARTTIME -- ENDTIME + if t.Unix() > startTime.Unix() && t.Unix() < endTime.Unix() { + return true + } + return false +} + +func (r *Rule) InSlice(str string, sli []string) bool { + for _, v := range sli { + if v == str { + return true + } + } + return false +} From 3d4ac1732a9942b3b1abddc7d411dbfdf2fb8fbb Mon Sep 17 00:00:00 2001 From: qingconglaixueit <502892037@qq.com> Date: Sat, 7 Jan 2023 16:06:40 +0800 Subject: [PATCH 34/37] [add]add code --- README.md | 2 +- config.dev.json | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f739960..05016e5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # wechatbot -> 本项目是 fork 他人的项目来进行学习和使用,请问商用,可以下载下来做自定义的功能 +> 本项目是 fork 他人的项目来进行学习和使用,请勿商用,可以下载下来做自定义的功能 > 最近ChatGPT异常火爆,本项目可以将个人微信化身GPT机器人, > 项目基于[openwechat](https://github.com/eatmoreapple/openwechat) 开发。 diff --git a/config.dev.json b/config.dev.json index 84bf81c..114234d 100644 --- a/config.dev.json +++ b/config.dev.json @@ -6,5 +6,7 @@ "model": "text-davinci-003", "temperature": 1, "reply_prefix": "来自机器人回复:", - "session_clear_token": "清空会话" + "session_clear_token": "清空会话", + "start_time": 9, + "end_time": 21 } From 575b0c1aa07fb574bd18be7ef098f502bf46b9e3 Mon Sep 17 00:00:00 2001 From: qingconglaixueit <502892037@qq.com> Date: Sun, 8 Jan 2023 08:52:59 +0800 Subject: [PATCH 35/37] =?UTF-8?q?[add]=20=E5=A2=9E=E5=8A=A0=20readme=20?= =?UTF-8?q?=E5=86=85=E5=AE=B9=EF=BC=8C=E7=A7=81=E8=81=8A=E4=B8=AD=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E6=8E=89=20vip=20=E7=9A=84=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++++- handlers/group_msg_handler.go | 1 - handlers/user_msg_handler.go | 9 ++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 05016e5..0df37ee 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ * 私聊回复前缀设置 * 好友添加自动通过 * 增加每天工作的起始时间和结束时间,只有在该时间段才会对外提供 chatgpt 服务 +* 增加 vip 用户在任意时段都可享受 chatgpt 服务,只需要在 \wechatbot\handlers\group_msg_handler.go 中 的 VipUserList 切片中, +加入具体的 vip 昵称 # 实现机制 目前机器人有两种实现方式 @@ -142,7 +144,9 @@ $ go run main.go "model": "text-davinci-003", "temperature": 1, "reply_prefix": "来自机器人回复:", - "session_clear_token": "清空会话" + "session_clear_token": "清空会话", + "start_time": 9, + "end_time": 21 } api_key:openai api_key diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 734cdcd..213371b 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -144,7 +144,6 @@ func (g *GroupMessageHandler) ReplyText() error { // 能服务的时候,才请求 chatgpt if isSvr { // 3.请求GPT获取回复 - //reply = "干你哦!!!" reply, err = gpt.Completions(requestText) if err != nil { // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index f8bc514..4b0c0e5 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -7,7 +7,6 @@ import ( "github.com/qingconglaixueit/wechatbot/config" "github.com/qingconglaixueit/wechatbot/gpt" "github.com/qingconglaixueit/wechatbot/pkg/logger" - "github.com/qingconglaixueit/wechatbot/rule" "github.com/qingconglaixueit/wechatbot/service" "strings" ) @@ -79,7 +78,7 @@ func (h *UserMessageHandler) ReplyText() error { } logger.Info(fmt.Sprintf("h.sender.NickName == %+v", h.sender.NickName)) // 只有 vip 用户才能私聊 - if rule.Grule.InSlice(h.sender.NickName, VipUserList) { + //if rule.Grule.InSlice(h.sender.NickName, VipUserList) { // 2.向GPT发起请求,如果回复文本等于空,不回复 reply, err = gpt.Completions(h.getRequestText()) if err != nil { @@ -91,9 +90,9 @@ func (h *UserMessageHandler) ReplyText() error { } return err } - } else { - reply = replyPersonal - } + //} else { + // reply = replyPersonal + //} // 2.设置上下文,回复用户 h.service.SetUserSessionContext(requestText, reply) From 326dc919d647d7e2cff57b422865c8b4f4bddafd Mon Sep 17 00:00:00 2001 From: qingconglaixueit <502892037@qq.com> Date: Mon, 9 Jan 2023 11:30:15 +0800 Subject: [PATCH 36/37] =?UTF-8?q?[modify]=20=20=E5=8E=BB=E6=8E=89=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E5=8A=9F=E8=83=BD,=20=E6=BA=90=E7=A0=81?= =?UTF-8?q?=E5=8C=85=E4=B8=8B=E8=BD=BD=E5=90=8E=E5=9C=A8=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E4=B8=8B=E6=89=A7=E8=A1=8C=20go=20mod=20tidy?= =?UTF-8?q?;go=20build=20=E5=8D=B3=E5=8F=AF=E7=BC=96=E8=AF=91=E5=87=BA?= =?UTF-8?q?=E5=8F=AF=E6=89=A7=E8=A1=8C=E7=A8=8B=E5=BA=8F=EF=BC=8C=E5=9C=A8?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E4=B8=AD=E5=A1=AB=E5=86=99?= =?UTF-8?q?=E8=87=AA=E5=B7=B1=E7=9A=84=20api=20keys=EF=BC=8C=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E5=8F=AF=E6=89=A7=E8=A1=8C=E7=A8=8B=E5=BA=8F=E5=8D=B3?= =?UTF-8?q?=E5=8F=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 +++---- config.dev.json | 4 +- config/config.go | 26 ----------- handlers/group_msg_handler.go | 84 +++++------------------------------ handlers/user_msg_handler.go | 23 ++++------ 5 files changed, 26 insertions(+), 127 deletions(-) diff --git a/README.md b/README.md index 0df37ee..840d6e7 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,6 @@ > `友链:`[chatgpt-dingtalk](https://github.com/eryajf/chatgpt-dingtalk) 本项目可以将GPT机器人集成到钉钉群聊中。 -[![Release](https://img.shields.io/github/v/release/869413421/wechatbot.svg?style=flat-square)](https://github.com/869413421/wechatbot/releases/tag/v1.1.3) -![Github stars](https://img.shields.io/github/stars/869413421/wechatbot.svg) -![Forks](https://img.shields.io/github/forks/869413421/wechatbot.svg?style=flat-square) ### 目前实现了以下功能 @@ -18,10 +15,10 @@ * 机器人群聊@回复 * 机器人私聊回复 * 私聊回复前缀设置 -* 好友添加自动通过 -* 增加每天工作的起始时间和结束时间,只有在该时间段才会对外提供 chatgpt 服务 -* 增加 vip 用户在任意时段都可享受 chatgpt 服务,只需要在 \wechatbot\handlers\group_msg_handler.go 中 的 VipUserList 切片中, -加入具体的 vip 昵称 +* 好友添加自动通过可配置 +* ~~增加每天工作的起始时间和结束时间,只有在该时间段才会对外提供 chatgpt 服务~~ +* ~~增加 vip 用户在任意时段都可享受 chatgpt 服务,只需要在 \wechatbot\handlers\group_msg_handler.go 中 的 VipUserList 切片中, +加入具体的 vip 昵称~~ # 实现机制 目前机器人有两种实现方式 @@ -42,6 +39,7 @@ > * ~~目前只支持在windows上运行因为需要弹窗扫码登录微信,后续会支持linux~~ 已支持 > * 有openai账号,并且创建好api_key,注册事项可以参考[此文章](https://juejin.cn/post/7173447848292253704) 。 +> * 应用可以参考这篇文章 [此文章](https://juejin.cn/post/7176813187705077816) 。 > * 微信必须实名认证。 # 注意事项 @@ -144,9 +142,7 @@ $ go run main.go "model": "text-davinci-003", "temperature": 1, "reply_prefix": "来自机器人回复:", - "session_clear_token": "清空会话", - "start_time": 9, - "end_time": 21 + "session_clear_token": "清空会话" } api_key:openai api_key diff --git a/config.dev.json b/config.dev.json index 114234d..84bf81c 100644 --- a/config.dev.json +++ b/config.dev.json @@ -6,7 +6,5 @@ "model": "text-davinci-003", "temperature": 1, "reply_prefix": "来自机器人回复:", - "session_clear_token": "清空会话", - "start_time": 9, - "end_time": 21 + "session_clear_token": "清空会话" } diff --git a/config/config.go b/config/config.go index 5c3a123..25b916a 100644 --- a/config/config.go +++ b/config/config.go @@ -29,10 +29,6 @@ type Configuration struct { ReplyPrefix string `json:"reply_prefix"` // 清空会话口令 SessionClearToken string `json:"session_clear_token"` - // 每天工作起始时间 - StartTime int `json:"start_time"` - // 每天工作结束时间 - EndTime int `json:"end_time"` } var config *Configuration @@ -49,8 +45,6 @@ func LoadConfig() *Configuration { Model: "text-davinci-003", Temperature: 0.9, SessionClearToken: "下一个问题", - StartTime: 9, - EndTime: 21, } // 判断配置文件是否存在,存在直接JSON读取 @@ -78,8 +72,6 @@ func LoadConfig() *Configuration { Temperature := os.Getenv("TEMPREATURE") ReplyPrefix := os.Getenv("REPLY_PREFIX") SessionClearToken := os.Getenv("SESSION_CLEAR_TOKEN") - StartTime := os.Getenv("START_TIME") - EndTime := os.Getenv("END_TIME") if ApiKey != "" { config.ApiKey = ApiKey } @@ -120,24 +112,6 @@ func LoadConfig() *Configuration { config.SessionClearToken = SessionClearToken } - if StartTime != "" { - sTime, err := strconv.Atoi(StartTime) - if err != nil { - logger.Warning("StartTime=%s strconv.Atoi error:%+v",StartTime,err) - }else{ - config.StartTime = sTime - } - } - - if EndTime != "" { - eTime, err := strconv.Atoi(EndTime) - if err != nil { - logger.Warning("EndTime=%s strconv.Atoi error:%+v",EndTime,err) - }else{ - config.EndTime = eTime - } - } - }) if config.ApiKey == "" { logger.Danger("config err: api key required") diff --git a/handlers/group_msg_handler.go b/handlers/group_msg_handler.go index 213371b..4fb5fde 100644 --- a/handlers/group_msg_handler.go +++ b/handlers/group_msg_handler.go @@ -4,30 +4,12 @@ import ( "errors" "fmt" "github.com/eatmoreapple/openwechat" - "github.com/qingconglaixueit/wechatbot/config" "github.com/qingconglaixueit/wechatbot/gpt" "github.com/qingconglaixueit/wechatbot/pkg/logger" - "github.com/qingconglaixueit/wechatbot/rule" "github.com/qingconglaixueit/wechatbot/service" "strings" ) -const ( - HaveARest = "该休息了" - WorkStr = "起来嗨" - WorkTime = "你的工作时间" - - replyForRest = "好的,我开始睡美容觉了!" - replyForWork = "主人,我现在活力满满!" - - replyForRest2 = "我已经休息了,你也快睡觉吧!" - - replyPersonal = "不要单独聊我哦,可以到群里互动哦!!" - - replyWorkTime = "我的工作时间是 %d 点 -- %d 点" -) - -var VipUserList = []string{"xxx", "xxx"} var _ MessageHandlerInterface = (*GroupMessageHandler)(nil) // GroupMessageHandler 群消息处理 @@ -113,52 +95,17 @@ func (g *GroupMessageHandler) ReplyText() error { logger.Info("user message is null") return nil } - logger.Info(fmt.Sprintf("fromname:%s requestText == %+v", g.sender.NickName, requestText)) - - if requestText == WorkTime { - reply = fmt.Sprintf(replyWorkTime, config.LoadConfig().StartTime, config.LoadConfig().EndTime) - } - - // 识别到是 Anonymous 发送过来的消息,且是 “该休息了!”,那么则将全局变量设置为 false,休息状态 - if rule.Grule.InSlice(g.sender.NickName, VipUserList) && requestText == HaveARest { - rule.Grule.SetWork(false) - reply = replyForRest - } - if rule.Grule.InSlice(g.sender.NickName, VipUserList) && requestText == WorkStr { - rule.Grule.SetWork(true) - reply = replyForWork - } - if rule.Grule.GetWork() && reply == "" { - isSvr := true - if !rule.Grule.IsWorkTime(config.LoadConfig().StartTime, config.LoadConfig().EndTime) { - isSvr = false - reply = replyForRest2 - } - // 非工作时间,仍然服务 vip 用户 - if !rule.Grule.IsWorkTime(config.LoadConfig().StartTime, config.LoadConfig().EndTime) && - rule.Grule.InSlice(g.sender.NickName, VipUserList) { - isSvr = true - reply = "" - } - // 能服务的时候,才请求 chatgpt - if isSvr { - // 3.请求GPT获取回复 - reply, err = gpt.Completions(requestText) - if err != nil { - // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 - errMsg := fmt.Sprintf("gpt request error: %v", err) - _, err = g.msg.ReplyText(errMsg) - if err != nil { - return errors.New(fmt.Sprintf("response group error: %v ", err)) - } - return err - } - } - } else { - if reply == "" { - reply = replyForRest2 + // 3.请求GPT获取回复 + reply, err = gpt.Completions(requestText) + if err != nil { + // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 + errMsg := fmt.Sprintf("gpt request error: %v", err) + _, err = g.msg.ReplyText(errMsg) + if err != nil { + return errors.New(fmt.Sprintf("response group error: %v ", err)) } + return err } // 4.设置上下文,并响应信息给用户 @@ -178,8 +125,6 @@ func (g *GroupMessageHandler) getRequestText() string { requestText := strings.TrimSpace(g.msg.Content) requestText = strings.Trim(g.msg.Content, "\n") - logger.Info(fmt.Sprintf("xxxxxxxxxxxxxxxx -- fromname:%s requestText == %+v", g.sender.NickName, requestText)) - // 2.替换掉当前用户名称 replaceText := "@" + g.self.NickName requestText = strings.TrimSpace(strings.ReplaceAll(g.msg.Content, replaceText, "")) @@ -187,16 +132,7 @@ func (g *GroupMessageHandler) getRequestText() string { return "" } - logger.Info(fmt.Sprintf("xxxxxxxxxxxxxxxx22 -- fromname:%s requestText==%+v", g.sender.NickName, requestText)) - - if requestText == WorkTime { - return requestText - } - if (requestText == WorkStr || requestText == HaveARest) && rule.Grule.InSlice(g.sender.NickName, VipUserList){ - return requestText - } - - // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算),达芬奇3最大为4068,也许后续为了适应要动态进行判断。 + // 3.获取上下文,拼接在一起,如果字符长度超出4000,截取为4000。(GPT按字符长度算),达芬奇3最大为4068,也许后续为了适应要动态进行判断。 sessionText := g.service.GetUserSessionContext() if sessionText != "" { requestText = sessionText + "\n" + requestText diff --git a/handlers/user_msg_handler.go b/handlers/user_msg_handler.go index 4b0c0e5..8558526 100644 --- a/handlers/user_msg_handler.go +++ b/handlers/user_msg_handler.go @@ -77,22 +77,17 @@ func (h *UserMessageHandler) ReplyText() error { return nil } logger.Info(fmt.Sprintf("h.sender.NickName == %+v", h.sender.NickName)) - // 只有 vip 用户才能私聊 - //if rule.Grule.InSlice(h.sender.NickName, VipUserList) { - // 2.向GPT发起请求,如果回复文本等于空,不回复 - reply, err = gpt.Completions(h.getRequestText()) + // 2.向GPT发起请求,如果回复文本等于空,不回复 + reply, err = gpt.Completions(h.getRequestText()) + if err != nil { + // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 + errMsg := fmt.Sprintf("gpt request error: %v", err) + _, err = h.msg.ReplyText(errMsg) if err != nil { - // 2.1 将GPT请求失败信息输出给用户,省得整天来问又不知道日志在哪里。 - errMsg := fmt.Sprintf("gpt request error: %v", err) - _, err = h.msg.ReplyText(errMsg) - if err != nil { - return errors.New(fmt.Sprintf("response user error: %v ", err)) - } - return err + return errors.New(fmt.Sprintf("response user error: %v ", err)) } - //} else { - // reply = replyPersonal - //} + return err + } // 2.设置上下文,回复用户 h.service.SetUserSessionContext(requestText, reply) From 605eb83ffde4ccc5770d2ec4299d677103162924 Mon Sep 17 00:00:00 2001 From: qingconglaixueit <502892037@qq.com> Date: Mon, 6 Mar 2023 20:33:40 +0800 Subject: [PATCH 37/37] [add]add gpt 3.5 --- go.mod | 7 +++++ go.sum | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gpt/gpt35.go | 41 ++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+) create mode 100644 gpt/gpt35.go diff --git a/go.mod b/go.mod index bb1bedb..391ae9b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,14 @@ module github.com/qingconglaixueit/wechatbot go 1.16 require ( + github.com/BurntSushi/toml v1.2.1 // indirect github.com/eatmoreapple/openwechat v1.2.1 + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/otiai10/openaigo v1.0.0 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/qingconglaixueit/abing_logger v0.0.2 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e + go.uber.org/zap v1.24.0 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 19f6633..fcc71fa 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,81 @@ +github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/eatmoreapple/openwechat v1.2.1 h1:ez4oqF/Y2NSEX/DbPV8lvj7JlfkYqvieeo4awx5lzfU= github.com/eatmoreapple/openwechat v1.2.1/go.mod h1:61HOzTyvLobGdgWhL68jfGNwTJEv0mhQ1miCXQrvWU8= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= +github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.4.1 h1:HOVBfKP1oXIc0wWo9hZ8JLdZtyCPWqjvmFDuVZ0yv2Y= +github.com/otiai10/mint v1.4.1/go.mod h1:gifjb2MYOoULtKLqUAEILUG/9KONW6f7YsJ6vQLTlFI= +github.com/otiai10/openaigo v1.0.0 h1:E4SoShWjMVltBAkM4/QsxGPhPKZlpKp0GSK6Ov1MSOg= +github.com/otiai10/openaigo v1.0.0/go.mod h1:792bx6AWTS61weDi2EzKpHHnTF4eDMAlJ5GvAk/mgPg= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/qingconglaixueit/abing_logger v0.0.2 h1:KPv+dzAwVYqB/nPViVy8YhQDORm7/ejQ4JSkAs8RnpA= +github.com/qingconglaixueit/abing_logger v0.0.2/go.mod h1:jXdlkJitAiZFx9IM9M0U1P+8myv0ieQRPznThQA6myg= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gpt/gpt35.go b/gpt/gpt35.go new file mode 100644 index 0000000..5ab0536 --- /dev/null +++ b/gpt/gpt35.go @@ -0,0 +1,41 @@ +// @Author Bing +// @Date 2023/3/6 20:31:00 +// @Desc +package gpt + +import ( + "context" + "github.com/otiai10/openaigo" + "github.com/qingconglaixueit/abing_logger" + "github.com/qingconglaixueit/wechatbot/config" +) + +type MyGpt struct { + C *openaigo.Client +} + +func NewGpr35() *openaigo.Client { + cfg := config.LoadConfig() + return openaigo.NewClient(cfg.ApiKey) +} + +func (c *MyGpt) Gpt3P5(req string) string { + request := openaigo.ChatCompletionRequestBody{ + Model: "gpt-3.5-turbo", + Messages: []openaigo.ChatMessage{ + {Role: "user", Content: req}, + }, + } + ctx := context.Background() + rsp, err := c.C.Chat(ctx, request) + if err != nil { + abing_logger.SugarLogger.Errorf("gpt client chat erorr:%+v", err) + return "" + } + + if len(rsp.Choices) == 0 || rsp.Choices[0].Message.Content == "" { + return "" + } + + return rsp.Choices[0].Message.Content +}