package tasks import ( "encoding/xml" "gitee.ltd/lxh/wechat-robot/internal/minio" "gitee.ltd/lxh/wechat-robot/internal/model" "gitee.ltd/lxh/wechat-robot/internal/types" "gitee.ltd/lxh/wechat-robot/internal/wechat" "gitee.ltd/lxh/xybot" "github.com/gofiber/fiber/v2/log" "strings" "time" ) // syncMessage // @description: 同步指定机器人的消息 // @param containerHost 机器人接口地址 // @param robotWxId 机器人微信Id // @param robotId 机器人数据Id func syncMessage(client *xybot.Client, robotId uint) { // 获取数据库连接 db := model.GetDB() // 获取新消息 messages, err := client.Message.Sync() if err != nil { // 手动处理一下是否离线了 isOffline := strings.Contains(err.Error(), "用户可能退出") || strings.Contains(err.Error(), "数据[DecryptData]失败") if isOffline { // 删除定时任务 DeleteJob(robotId) // 修改机器人状态 db.Model(&model.Robot{}).Where("id = ?", robotId).Update("status", "offline") } return } if len(messages) == 0 { // 没有消息,直接返回 return } //log.Debugf("当前同步机器人Id: %d,消息数量: %d,客户端信息: %+v", robotId, len(messages), client) // 处理消息 var msg []model.Message for _, message := range messages { //log.Debugf("当前同步机器人Id: %d,消息内容: %s", robotId, message.Content.String) var m model.Message m.RobotId = robotId m.MsgId = message.NewMsgId m.ClientMsgId = message.MsgId m.CreateTime = message.CreateTime m.CreateAt = time.Unix(int64(message.CreateTime), 0) m.Type = types.MessageType(message.MsgType) m.Content = message.Content.String m.DisplayFullContent = message.PushContent m.FromUser = message.FromUserName.String m.ToUser = message.ToWxid.String // 如果是群聊消息,单独处理一下 // Sys类型的消息正文不包含微信 Id,所以不需要处理 if strings.HasSuffix(message.FromUserName.String, "@chatroom") && m.Type != types.MsgTypeSys { // 群消息,处理一下消息和发信人 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") } // 处理一下,如果是图片、文件、表情包等信息,直接下载下来存到OSS if m.Type == types.MsgTypeImage || m.Type == types.MsgTypeVideo { // 解析消息xml文件 // 解析为结构体 var media wechat.MediaMessage if err = xml.Unmarshal([]byte(m.Content), &media); err != nil { log.Errorf("%v解析消息失败: %v", m.Type, err.Error()) continue } var md5Str, bs64Str string switch m.Type { case types.MsgTypeImage: fileUrl := "" md5Str = media.Img.Md5 if media.Img.CdnBigImgUrl != "" { fileUrl = media.Img.CdnBigImgUrl } else if media.Img.CdnMidImgUrl != "" { fileUrl = media.Img.CdnMidImgUrl } else if media.Img.CdnThumbUrl != "" { fileUrl = media.Img.CdnThumbUrl } else { continue } if bs64Str, err = client.Tool.CdnDownloadImg(media.Img.AesKey, fileUrl); err != nil { log.Errorf("图片文件下载失败: %s", err.Error()) continue } case types.MsgTypeVideo: if bs64Str, err = client.Tool.DownloadVideo(m.ClientMsgId); err != nil { log.Errorf("视频文件下载失败: %s", err.Error()) continue } } // 下载完成,保存到OSS if m.FileUrl, err = minio.SaveBase64(bs64Str, md5Str); err != nil { log.Errorf("文件保存到Minio失败: %s", err.Error()) continue } } msg = append(msg, m) } // 保存入库 db.Save(&msg) }