diff --git a/handler/at_message.go b/handler/at_message.go index a2a158f6..4a0d3949 100644 --- a/handler/at_message.go +++ b/handler/at_message.go @@ -4,9 +4,10 @@ import ( "context" "fmt" "github.com/sashabaranov/go-openai" + "go-wechat/client" "go-wechat/config" "go-wechat/entity" - "go-wechat/service" + "go-wechat/model" "go-wechat/utils" "log" "regexp" @@ -16,26 +17,15 @@ import ( // handleAtMessage // @description: 处理At机器人的消息 // @param m -func handleAtMessage(m entity.Message) { +func handleAtMessage(m model.Message) { if !config.Conf.Ai.Enable { return } // 取出所有启用了AI的好友或群组 - us, err := service.GetAllEnableAI() - if err != nil { - utils.SendMessage(m.FromUser, m.GroupUser, "#系统异常\n"+err.Error(), 0) - return - } - // 判断是否启用,如果没有启用,直接返回 - var canUse bool - for _, u := range us { - if u.Wxid == m.FromUser { - canUse = true - break - } - } - if !canUse { + var count int64 + client.MySQL.Model(&entity.Friend{}).Where("enable_ai IS TRUE").Where("wxid = ?", m.FromUser).Count(&count) + if count < 1 { return } @@ -63,9 +53,9 @@ func handleAtMessage(m entity.Message) { }) // 配置模型 - model := openai.GPT3Dot5Turbo0613 + chatModel := openai.GPT3Dot5Turbo0613 if config.Conf.Ai.Model != "" { - model = config.Conf.Ai.Model + chatModel = config.Conf.Ai.Model } // 默认使用AI回复 @@ -77,7 +67,7 @@ func handleAtMessage(m entity.Message) { resp, err := client.CreateChatCompletion( context.Background(), openai.ChatCompletionRequest{ - Model: model, + Model: chatModel, Messages: messages, }, ) diff --git a/handler/parse.go b/handler/parse.go index 98966e78..c81b7542 100644 --- a/handler/parse.go +++ b/handler/parse.go @@ -6,6 +6,7 @@ import ( "go-wechat/model" "go-wechat/service" "go-wechat/types" + "go-wechat/utils" "log" "net" "strings" @@ -23,43 +24,47 @@ func Parse(remoteAddr net.Addr, msg []byte) { return } // 提取出群成员信息 - groupUser := "" - msgStr := m.Content + //groupUser := "" + //msgStr := m.Content if strings.Contains(m.FromUser, "@") { - switch m.Type { - case types.MsgTypeRecalled: - // 消息撤回 - case types.MsgTypeSys: - // 系统消息 - go handleSysMessage(m) - default: - // 默认消息处理 - groupUser = strings.Split(m.Content, "\n")[0] - groupUser = strings.ReplaceAll(groupUser, ":", "") - - // 文字消息单独提出来处理一下 - msgStr = strings.Join(strings.Split(m.Content, "\n")[1:], "\n") + // 群消息,处理一下消息和发信人 + groupUser := strings.Split(m.Content, "\n")[0] + groupUser = strings.ReplaceAll(groupUser, ":", "") + // 如果两个id一致,说明是系统发的 + if m.FromUser != groupUser { + m.GroupUser = groupUser } + // 用户的操作单独提出来处理一下 + m.Content = strings.Join(strings.Split(m.Content, "\n")[1:], "\n") } - log.Printf("%s\n消息来源: %s\n群成员: %s\n消息类型: %v\n消息内容: %s", remoteAddr, m.FromUser, groupUser, m.Type, msgStr) + log.Printf("%s\n消息来源: %s\n群成员: %s\n消息类型: %v\n消息内容: %s", remoteAddr, m.FromUser, m.GroupUser, m.Type, m.Content) + + // 异步处理消息 + go func() { + if m.IsNewUserJoin() { + // 欢迎新成员 + go handleNewUserJoin(m) + } else if m.IsAt() { + // @机器人的消息 + go handleAtMessage(m) + } else if !strings.Contains(m.FromUser, "@") && m.Type == types.MsgTypeText { + // 私聊消息处理 + utils.SendMessage(m.FromUser, "", "暂未开启私聊AI", 0) + } + }() // 转换为结构体之后入库 var ent entity.Message ent.MsgId = m.MsgId ent.CreateTime = m.CreateTime ent.CreateAt = time.Unix(int64(m.CreateTime), 0) - ent.Content = msgStr + ent.Content = m.Content ent.FromUser = m.FromUser - ent.GroupUser = groupUser + ent.GroupUser = m.GroupUser ent.ToUser = m.ToUser ent.Type = m.Type ent.DisplayFullContent = m.DisplayFullContent ent.Raw = string(msg) - // 处理At机器人的消息 - if strings.HasSuffix(m.DisplayFullContent, "在群聊中@了你") { - go handleAtMessage(ent) - } - go service.SaveMessage(ent) } diff --git a/model/message.go b/model/message.go index cc7fee1c..1465c7a3 100644 --- a/model/message.go +++ b/model/message.go @@ -1,18 +1,87 @@ package model -import "go-wechat/types" +import ( + "encoding/xml" + "go-wechat/types" + "strings" +) // Message // @description: 消息 type Message struct { - MsgId int64 `json:"msgId" gorm:"primarykey"` + MsgId int64 `json:"msgId"` CreateTime int `json:"createTime"` Content string `json:"content"` - DisplayFullContent string `json:"displayFullContent" gorm:"-"` + DisplayFullContent string `json:"displayFullContent"` FromUser string `json:"fromUser"` + GroupUser string `json:"-"` MsgSequence int `json:"msgSequence"` Pid int `json:"pid"` Signature string `json:"signature"` ToUser string `json:"toUser"` Type types.MessageType `json:"type"` } + +// systemMsgDataXml +// @description: 微信系统消息的xml结构 +type systemMsgDataXml struct { + SysMsg sysMsg `xml:"sysmsg"` + Type string `xml:"type,attr"` +} + +// sysMsg +// @description: 消息主体 +type sysMsg struct{} + +// IsPat +// @description: 是否是拍一拍消息 +// @receiver m +// @return bool +func (m Message) IsPat() bool { + // 解析xml + var d systemMsgDataXml + if err := xml.Unmarshal([]byte(m.Content), &d); err != nil { + return false + } + + return m.Type == types.MsgTypeRecalled && d.Type == "pat" +} + +// IsRevokeMsg +// @description: 是否是撤回消息 +// @receiver m +// @return bool +func (m Message) IsRevokeMsg() bool { + // 解析xml + var d systemMsgDataXml + if err := xml.Unmarshal([]byte(m.Content), &d); err != nil { + return false + } + + return m.Type == types.MsgTypeRecalled && d.Type == "revokemsg" +} + +// IsNewUserJoin +// @description: 是否是新人入群 +// @receiver m +// @return bool +func (m Message) IsNewUserJoin() bool { + sysFlag := m.Type == types.MsgTypeSys && strings.Contains(m.Content, "\"邀请\"") && strings.Contains(m.Content, "\"加入了群聊") + if sysFlag { + return true + } + // 解析另一种情况 + var d systemMsgDataXml + if err := xml.Unmarshal([]byte(m.Content), &d); err != nil { + return false + } + return m.Type == types.MsgTypeSys && d.Type == "delchatroommember" +} + +// IsAt +// @description: 是否是At机器人的消息 +// @receiver m +// @return bool +func (m Message) IsAt() bool { + return strings.HasSuffix(m.DisplayFullContent, "在群聊中@了你") +} diff --git a/types/message.go b/types/message.go index f5268d96..b0eb39ca 100644 --- a/types/message.go +++ b/types/message.go @@ -4,6 +4,7 @@ import "fmt" type MessageType int +// 微信定义的消息类型 const ( MsgTypeText MessageType = 1 // 文本消息 MsgTypeImage MessageType = 3 // 图片消息