From 29fc9243d6bc58c869012e9bb297a784246e8aae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B8=AB=E8=AE=B2=E6=A2=B5?= Date: Tue, 4 Apr 2023 14:30:13 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E8=AF=81=E4=B9=A6=E4=BF=A1=E6=81=AF=E7=9A=84?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E8=83=BD=E5=8A=9B=20(#170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.go | 4 ++ pkg/ops/opstools.go | 66 +++++++++++++++++++++ pkg/process/db.go | 67 ++++++++++++++++++++++ pkg/process/image.go | 62 ++++++++++++++++++++ pkg/process/opstools.go | 95 ++++++++++++++++++++++++++++++ pkg/process/process_request.go | 102 --------------------------------- public/tools.go | 5 ++ 7 files changed, 299 insertions(+), 102 deletions(-) create mode 100644 pkg/ops/opstools.go create mode 100644 pkg/process/db.go create mode 100644 pkg/process/image.go create mode 100644 pkg/process/opstools.go diff --git a/main.go b/main.go index b8b5bff..ddeacc1 100644 --- a/main.go +++ b/main.go @@ -72,6 +72,10 @@ func Start() { return process.ImageGenerate(&msgObj) case strings.HasPrefix(msgObj.Text.Content, "#查对话"): return process.SelectHistory(&msgObj) + case strings.HasPrefix(msgObj.Text.Content, "#域名"): + return process.DomainMsg(&msgObj) + case strings.HasPrefix(msgObj.Text.Content, "#证书"): + return process.DomainCertMsg(&msgObj) default: msgObj.Text.Content, err = process.GeneratePrompt(msgObj.Text.Content) // err不为空:提示词之后没有文本 -> 直接返回提示词所代表的内容 diff --git a/pkg/ops/opstools.go b/pkg/ops/opstools.go new file mode 100644 index 0000000..b140e35 --- /dev/null +++ b/pkg/ops/opstools.go @@ -0,0 +1,66 @@ +package ops + +import ( + "crypto/tls" + "net" + "regexp" + "strings" + "time" +) + +// 域名信息 +type DomainMsg struct { + CreateDate string `json:"create_date"` + ExpiryDate string `json:"expiry_date"` + Registrar string `json:"registrar"` +} + +// GetDomainMsg 获取域名信息 +func GetDomainMsg(domain string) (dm DomainMsg, err error) { + var conn net.Conn + conn, err = net.Dial("tcp", "whois.verisign-grs.com:43") + if err != nil { + return + } + defer conn.Close() + + _, err = conn.Write([]byte(domain + "\r\n")) + if err != nil { + return + } + buf := make([]byte, 1024) + var num int + num, err = conn.Read(buf) + if err != nil { + return + } + response := string(buf[:num]) + re := regexp.MustCompile(`Creation Date: (.*)\n.*Expiry Date: (.*)\n.*Registrar: (.*)`) + match := re.FindStringSubmatch(response) + if len(match) > 3 { + dm.CreateDate = strings.TrimSpace(strings.Split(match[1], "Creation Date:")[0]) + dm.ExpiryDate = strings.TrimSpace(strings.Split(match[2], "Expiry Date:")[0]) + dm.Registrar = strings.TrimSpace(strings.Split(match[3], "Registrar:")[0]) + } + return +} + +// GetDomainCertMsg 获取域名证书信息 +func GetDomainCertMsg(domain string) (cm tls.ConnectionState, err error) { + var conn net.Conn + conn, err = net.DialTimeout("tcp", domain+":443", time.Second*10) + if err != nil { + return + } + defer conn.Close() + tlsConn := tls.Client(conn, &tls.Config{ + ServerName: domain, + }) + defer tlsConn.Close() + err = tlsConn.Handshake() + if err != nil { + return + } + cm = tlsConn.ConnectionState() + return +} diff --git a/pkg/process/db.go b/pkg/process/db.go new file mode 100644 index 0000000..d85824b --- /dev/null +++ b/pkg/process/db.go @@ -0,0 +1,67 @@ +package process + +import ( + "fmt" + "strings" + "time" + + "github.com/eryajf/chatgpt-dingtalk/pkg/db" + "github.com/eryajf/chatgpt-dingtalk/pkg/dingbot" + "github.com/eryajf/chatgpt-dingtalk/pkg/logger" + "github.com/eryajf/chatgpt-dingtalk/public" +) + +// 与数据库交互的请求处理在此 + +// SelectHistory 查询会话历史 +func SelectHistory(rmsg *dingbot.ReceiveMsg) error { + name := strings.TrimSpace(strings.Split(rmsg.Text.Content, ":")[1]) + if !public.JudgeAdminUsers(rmsg.SenderNick) { + _, err := rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您没有查询对话记录的权限,只有程序管理员可以查询!**") + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + return nil + } + // 获取数据列表 + var chat db.Chat + if !chat.Exist(map[string]interface{}{"username": name}) { + _, err := rmsg.ReplyToDingtalk(string(dingbot.TEXT), "用户名错误,这个用户不存在,请核实之后再进行查询") + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + return fmt.Errorf("用户名错误,这个用户不存在,请核实之后重新查询") + } + chats, err := chat.List(db.ChatListReq{ + Username: name, + }) + if err != nil { + return err + } + var rst string + for _, chatTmp := range chats { + ctime := chatTmp.CreatedAt.Format("2006-01-02 15:04:05") + if chatTmp.ChatType == 1 { + rst += fmt.Sprintf("## 🙋 %s 问\n\n**时间:** %v\n\n**问题为:** %s\n\n", chatTmp.Username, ctime, chatTmp.Content) + } else { + rst += fmt.Sprintf("## 🤖 机器人答\n\n**时间:** %v\n\n**回答如下:** \n\n%s\n\n", ctime, chatTmp.Content) + } + // TODO: 答案应该严格放在问题之后,目前只根据ID排序进行的陈列,当一个用户同时提出多个问题时,最终展示的可能会有点问题 + } + fileName := time.Now().Format("20060102-150405") + ".md" + // 写入文件 + if err = public.WriteToFile("./data/chatHistory/"+fileName, []byte(rst)); err != nil { + return err + } + // 回复@我的用户 + reply := fmt.Sprintf("- 在线查看: [点我](%s)\n- 下载文件: [点我](%s)\n- 在线预览请安装插件:[Markdown Preview Plus](https://chrome.google.com/webstore/detail/markdown-preview-plus/febilkbfcbhebfnokafefeacimjdckgl)", public.Config.ServiceURL+"/history/"+fileName, public.Config.ServiceURL+"/download/"+fileName) + logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply)) + _, err = rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), reply) + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + return nil +} diff --git a/pkg/process/image.go b/pkg/process/image.go new file mode 100644 index 0000000..76c93a3 --- /dev/null +++ b/pkg/process/image.go @@ -0,0 +1,62 @@ +package process + +import ( + "fmt" + "strings" + + "github.com/eryajf/chatgpt-dingtalk/pkg/db" + "github.com/eryajf/chatgpt-dingtalk/pkg/dingbot" + "github.com/eryajf/chatgpt-dingtalk/pkg/logger" + "github.com/solywsh/chatgpt" +) + +// ImageGenerate openai生成图片 +func ImageGenerate(rmsg *dingbot.ReceiveMsg) error { + qObj := db.Chat{ + Username: rmsg.SenderNick, + Source: rmsg.GetChatTitle(), + ChatType: db.Q, + ParentContent: 0, + Content: rmsg.Text.Content, + } + qid, err := qObj.Add() + if err != nil { + logger.Error("往MySQL新增数据失败,错误信息:", err) + } + reply, err := chatgpt.ImageQa(rmsg.Text.Content, rmsg.GetSenderIdentifier()) + if err != nil { + logger.Info(fmt.Errorf("gpt request error: %v", err)) + _, err = rmsg.ReplyToDingtalk(string(dingbot.TEXT), fmt.Sprintf("请求openai失败了,错误信息:%v", err)) + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + } + if reply == "" { + logger.Warning(fmt.Errorf("get gpt result falied: %v", err)) + return nil + } else { + reply = strings.TrimSpace(reply) + reply = strings.Trim(reply, "\n") + reply = fmt.Sprintf(">点击图片可旋转或放大。\n![](%s)", reply) + aObj := db.Chat{ + Username: rmsg.SenderNick, + Source: rmsg.GetChatTitle(), + ChatType: db.A, + ParentContent: qid, + Content: reply, + } + _, err := aObj.Add() + if err != nil { + logger.Error("往MySQL新增数据失败,错误信息:", err) + } + logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply)) + // 回复@我的用户 + _, err = rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), reply) + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + } + return nil +} diff --git a/pkg/process/opstools.go b/pkg/process/opstools.go new file mode 100644 index 0000000..4c99cde --- /dev/null +++ b/pkg/process/opstools.go @@ -0,0 +1,95 @@ +package process + +import ( + "fmt" + "strings" + + "github.com/eryajf/chatgpt-dingtalk/pkg/db" + "github.com/eryajf/chatgpt-dingtalk/pkg/dingbot" + "github.com/eryajf/chatgpt-dingtalk/pkg/logger" + "github.com/eryajf/chatgpt-dingtalk/pkg/ops" + "github.com/eryajf/chatgpt-dingtalk/public" +) + +// 一些运维方面的工具在此 + +// 域名信息 +func DomainMsg(rmsg *dingbot.ReceiveMsg) error { + qObj := db.Chat{ + Username: rmsg.SenderNick, + Source: rmsg.GetChatTitle(), + ChatType: db.Q, + ParentContent: 0, + Content: rmsg.Text.Content, + } + qid, err := qObj.Add() + if err != nil { + logger.Error("往MySQL新增数据失败,错误信息:", err) + } + domain := strings.TrimSpace(strings.Split(rmsg.Text.Content, " ")[1]) + dm, err := ops.GetDomainMsg(domain) + if err != nil { + return err + } + // 回复@我的用户 + reply := fmt.Sprintf("**创建时间:** %v\n\n**到期时间:** %v\n\n**服务商:** %v", dm.CreateDate, dm.ExpiryDate, dm.Registrar) + aObj := db.Chat{ + Username: rmsg.SenderNick, + Source: rmsg.GetChatTitle(), + ChatType: db.A, + ParentContent: qid, + Content: reply, + } + _, err = aObj.Add() + if err != nil { + logger.Error("往MySQL新增数据失败,错误信息:", err) + } + logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply)) + _, err = rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), reply) + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + return nil +} + +// 证书信息 +func DomainCertMsg(rmsg *dingbot.ReceiveMsg) error { + qObj := db.Chat{ + Username: rmsg.SenderNick, + Source: rmsg.GetChatTitle(), + ChatType: db.Q, + ParentContent: 0, + Content: rmsg.Text.Content, + } + qid, err := qObj.Add() + if err != nil { + logger.Error("往MySQL新增数据失败,错误信息:", err) + } + domain := strings.TrimSpace(strings.Split(rmsg.Text.Content, " ")[1]) + dm, err := ops.GetDomainCertMsg(domain) + if err != nil { + return err + } + cert := dm.PeerCertificates[0] + // 回复@我的用户 + reply := fmt.Sprintf("**证书创建时间:** %v\n\n**证书到期时间:** %v\n\n**证书颁发机构:** %v\n\n", public.GetReadTime(cert.NotBefore), public.GetReadTime(cert.NotAfter), cert.Issuer.Organization) + aObj := db.Chat{ + Username: rmsg.SenderNick, + Source: rmsg.GetChatTitle(), + ChatType: db.A, + ParentContent: qid, + Content: reply, + } + _, err = aObj.Add() + if err != nil { + logger.Error("往MySQL新增数据失败,错误信息:", err) + } + logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply)) + _, err = rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), reply) + if err != nil { + logger.Error(fmt.Errorf("send message error: %v", err)) + return err + } + return nil +} diff --git a/pkg/process/process_request.go b/pkg/process/process_request.go index 8d6c059..17f6637 100644 --- a/pkg/process/process_request.go +++ b/pkg/process/process_request.go @@ -3,7 +3,6 @@ package process import ( "fmt" "strings" - "time" "github.com/eryajf/chatgpt-dingtalk/pkg/db" "github.com/eryajf/chatgpt-dingtalk/pkg/dingbot" @@ -212,104 +211,3 @@ func Do(mode string, rmsg *dingbot.ReceiveMsg) error { } return nil } - -func ImageGenerate(rmsg *dingbot.ReceiveMsg) error { - qObj := db.Chat{ - Username: rmsg.SenderNick, - Source: rmsg.GetChatTitle(), - ChatType: db.Q, - ParentContent: 0, - Content: rmsg.Text.Content, - } - qid, err := qObj.Add() - if err != nil { - logger.Error("往MySQL新增数据失败,错误信息:", err) - } - reply, err := chatgpt.ImageQa(rmsg.Text.Content, rmsg.GetSenderIdentifier()) - if err != nil { - logger.Info(fmt.Errorf("gpt request error: %v", err)) - _, err = rmsg.ReplyToDingtalk(string(dingbot.TEXT), fmt.Sprintf("请求openai失败了,错误信息:%v", err)) - if err != nil { - logger.Error(fmt.Errorf("send message error: %v", err)) - return err - } - } - if reply == "" { - logger.Warning(fmt.Errorf("get gpt result falied: %v", err)) - return nil - } else { - reply = strings.TrimSpace(reply) - reply = strings.Trim(reply, "\n") - reply = fmt.Sprintf(">点击图片可旋转或放大。\n![](%s)", reply) - aObj := db.Chat{ - Username: rmsg.SenderNick, - Source: rmsg.GetChatTitle(), - ChatType: db.A, - ParentContent: qid, - Content: reply, - } - _, err := aObj.Add() - if err != nil { - logger.Error("往MySQL新增数据失败,错误信息:", err) - } - logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply)) - // 回复@我的用户 - _, err = rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), reply) - if err != nil { - logger.Error(fmt.Errorf("send message error: %v", err)) - return err - } - } - return nil -} -func SelectHistory(rmsg *dingbot.ReceiveMsg) error { - name := strings.TrimSpace(strings.Split(rmsg.Text.Content, ":")[1]) - if !public.JudgeAdminUsers(rmsg.SenderNick) { - _, err := rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), "**🤷 抱歉,您没有查询对话记录的权限,只有程序管理员可以查询!**") - if err != nil { - logger.Error(fmt.Errorf("send message error: %v", err)) - return err - } - return nil - } - // 获取数据列表 - var chat db.Chat - if !chat.Exist(map[string]interface{}{"username": name}) { - _, err := rmsg.ReplyToDingtalk(string(dingbot.TEXT), "用户名错误,这个用户不存在,请核实之后再进行查询") - if err != nil { - logger.Error(fmt.Errorf("send message error: %v", err)) - return err - } - return fmt.Errorf("用户名错误,这个用户不存在,请核实之后重新查询") - } - chats, err := chat.List(db.ChatListReq{ - Username: name, - }) - if err != nil { - return err - } - var rst string - for _, chatTmp := range chats { - ctime := chatTmp.CreatedAt.Format("2006-01-02 15:04:05") - if chatTmp.ChatType == 1 { - rst += fmt.Sprintf("## 🙋 %s 问\n\n**时间:** %v\n\n**问题为:** %s\n\n", chatTmp.Username, ctime, chatTmp.Content) - } else { - rst += fmt.Sprintf("## 🤖 机器人答\n\n**时间:** %v\n\n**回答如下:** \n\n%s\n\n", ctime, chatTmp.Content) - } - // TODO: 答案应该严格放在问题之后,目前只根据ID排序进行的陈列,当一个用户同时提出多个问题时,最终展示的可能会有点问题 - } - fileName := time.Now().Format("20060102-150405") + ".md" - // 写入文件 - if err = public.WriteToFile("./data/chatHistory/"+fileName, []byte(rst)); err != nil { - return err - } - // 回复@我的用户 - reply := fmt.Sprintf("- 在线查看: [点我](%s)\n- 下载文件: [点我](%s)\n- 在线预览请安装插件:[Markdown Preview Plus](https://chrome.google.com/webstore/detail/markdown-preview-plus/febilkbfcbhebfnokafefeacimjdckgl)", public.Config.ServiceURL+"/history/"+fileName, public.Config.ServiceURL+"/download/"+fileName) - logger.Info(fmt.Sprintf("🤖 %s得到的答案: %#v", rmsg.SenderNick, reply)) - _, err = rmsg.ReplyToDingtalk(string(dingbot.MARKDOWN), reply) - if err != nil { - logger.Error(fmt.Errorf("send message error: %v", err)) - return err - } - return nil -} diff --git a/public/tools.go b/public/tools.go index f1aa0fd..5115652 100644 --- a/public/tools.go +++ b/public/tools.go @@ -4,6 +4,7 @@ import ( "io/ioutil" "os" "strings" + "time" ) // 将内容写入到文件,如果文件名带路径,则会判断路径是否存在,不存在则创建 @@ -63,3 +64,7 @@ func JudgeAdminUsers(s string) bool { } return false } + +func GetReadTime(t time.Time) string { + return t.Format("2006-01-02 15:04:05") +}