package model import ( "encoding/xml" "github.com/duke-git/lancet/v2/slice" "go-wechat/types" "regexp" "strings" ) // Message // @description: 消息 type Message struct { MsgId int64 `json:"msgId"` CreateTime int `json:"createTime"` Content string `json:"content"` 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"` Raw string `json:"raw"` } // systemMsgDataXml // @description: 微信系统消息的xml结构 type systemMsgDataXml struct { SysMsg sysMsg `xml:"sysmsg"` Type string `xml:"type,attr"` } // atMsgDataXml // @description: 微信@消息的xml结构 type atMsgDataXml struct { XMLName xml.Name `xml:"msgsource"` Text string `xml:",chardata"` AtUserList string `xml:"atuserlist"` Silence string `xml:"silence"` MemberCount string `xml:"membercount"` Signature string `xml:"signature"` TmpNode struct { Text string `xml:",chardata"` PublisherID string `xml:"publisher-id"` } `xml:"tmp_node"` } // sysMsg // @description: 消息主体 type sysMsg struct{} func (m Message) IsGroup() bool { return strings.HasSuffix(m.FromUser, "@chatroom") } // 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, "在群聊中@了你") } // IsAtAll // @description: 是否是At所有人的消息 // @receiver m // @return bool func (m Message) IsAtAll() bool { // 解析raw里面的xml var d atMsgDataXml if err := xml.Unmarshal([]byte(m.Signature), &d); err != nil { return false } // 转换@用户列表为数组 atUserList := strings.Split(d.AtUserList, ",") // 判断是否包含@所有人 return slice.Contain(atUserList, "notify@all") } // IsPrivateText // @description: 是否是私聊消息 // @receiver m // @return bool func (m Message) IsPrivateText() bool { // 发信人不以@chatroom结尾且消息类型为文本 return !strings.HasSuffix(m.FromUser, "chatroom") && m.Type == types.MsgTypeText } // CleanContentStartWith // @description: 判断是否包含指定消息前缀 // @receiver m // @param prefix // @return bool func (m Message) CleanContentStartWith(prefix string) bool { content := m.Content // 如果是@消息,过滤掉@的内容 if m.IsAt() { re := regexp.MustCompile(`@([^ | ]+)`) matches := re.FindStringSubmatch(content) if len(matches) > 0 { // 过滤掉第一个匹配到的 content = strings.Replace(content, matches[0], "", 1) } } // 去掉最前面的空格 content = strings.TrimLeft(content, " ") content = strings.TrimLeft(content, " ") return strings.HasPrefix(content, prefix) }