diff --git a/coolq/api.go b/coolq/api.go index b3efcaf..929faa8 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -540,7 +540,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, true) + elem = bot.ConvertObjectMessage(m, MessageSourceGroup) } else { str := m.String() if str == "" { @@ -550,7 +550,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, true) + elem = bot.ConvertStringMessage(str, MessageSourceGroup) } } fixAt(elem) @@ -580,9 +580,8 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R return Failed(100, "CHANNEL_NOT_SUPPORTED_TEXT_MSG", "子频道类型错误, 无法发送文本信息") } var elem []message.IMessageElement - // todo: 将 converter 适配频道或者为频道单独写一个 if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, true) + elem = bot.ConvertObjectMessage(m, MessageSourceGuildChannel) } else { str := m.String() if str == "" { @@ -592,7 +591,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, true) + elem = bot.ConvertStringMessage(str, MessageSourceGuildChannel) } } mid := bot.SendGuildChannelMessage(guildID, channelID, &message.SendingMessage{Elements: elem}) @@ -658,7 +657,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa } return int32(msgTime) }(), - Message: resolveElement(bot.ConvertContentMessage(m.Content, true)), + Message: resolveElement(bot.ConvertContentMessage(m.Content, MessageSourceGroup)), } } log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str) @@ -697,7 +696,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa } } } - content := bot.ConvertObjectMessage(e.Get("data.content"), true) + content := bot.ConvertObjectMessage(e.Get("data.content"), MessageSourceGroup) if uin != 0 && name != "" && len(content) > 0 { return &message.ForwardNode{ SenderId: uin, @@ -744,7 +743,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG { var elem []message.IMessageElement if m.Type == gjson.JSON { - elem = bot.ConvertObjectMessage(m, false) + elem = bot.ConvertObjectMessage(m, MessageSourcePrivate) } else { str := m.String() if str == "" { @@ -753,7 +752,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Resu if autoEscape { elem = []message.IMessageElement{message.NewText(str)} } else { - elem = bot.ConvertStringMessage(str, false) + elem = bot.ConvertStringMessage(str, MessageSourcePrivate) } } mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem}) @@ -1339,7 +1338,7 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG { "nickname": n.SenderName, }, "time": n.Time, - "content": ToFormattedMessage(n.Message, 0, false), + "content": ToFormattedMessage(n.Message, MessageSource{SourceType: MessageSourceGroup}, false), }) } return OK(global.MSG{ @@ -1373,9 +1372,9 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { switch o := msg.(type) { case *db.StoredGroupMessage: m["group_id"] = o.GroupCode - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, true), o.GroupCode, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, MessageSourceGroup), MessageSource{SourceType: MessageSourceGroup, PrimaryID: uint64(o.GroupCode)}, false) case *db.StoredPrivateMessage: - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, false), 0, false) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, MessageSourcePrivate), MessageSource{SourceType: MessageSourcePrivate}, false) } return OK(m) } @@ -1474,7 +1473,7 @@ func (bot *CQBot) CQCanSendRecord() global.MSG { // @alias(.ocr_image) // @rename(image_id->image) func (bot *CQBot) CQOcrImage(imageID string) global.MSG { - img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, true) + img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, MessageSourceGroup) if err != nil { log.Warnf("load image error: %v", err) return Failed(100, "LOAD_FILE_ERROR", err.Error()) diff --git a/coolq/bot.go b/coolq/bot.go index f54f8b3..4a9f702 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -523,76 +523,6 @@ func (bot *CQBot) dispatchEventMessage(m global.MSG) { global.PutBuffer(event.buffer) } -func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { - cqm := ToStringMessage(m.Elements, m.GroupCode, true) - gm := global.MSG{ - "anonymous": nil, - "font": 0, - "group_id": m.GroupCode, - "message": ToFormattedMessage(m.Elements, m.GroupCode, false), - "message_type": "group", - "message_seq": m.Id, - "post_type": func() string { - if m.Sender.Uin == bot.Client.Uin { - return "message_sent" - } - return "message" - }(), - "raw_message": cqm, - "self_id": bot.Client.Uin, - "sender": global.MSG{ - "age": 0, - "area": "", - "level": "", - "sex": "unknown", - "user_id": m.Sender.Uin, - }, - "sub_type": "normal", - "time": m.Time, - "user_id": m.Sender.Uin, - } - if m.Sender.IsAnonymous() { - gm["anonymous"] = global.MSG{ - "flag": m.Sender.AnonymousInfo.AnonymousId + "|" + m.Sender.AnonymousInfo.AnonymousNick, - "id": m.Sender.Uin, - "name": m.Sender.AnonymousInfo.AnonymousNick, - } - gm["sender"].(global.MSG)["nickname"] = "匿名消息" - gm["sub_type"] = "anonymous" - } else { - group := bot.Client.FindGroup(m.GroupCode) - mem := group.FindMember(m.Sender.Uin) - if mem == nil { - log.Warnf("获取 %v 成员信息失败,尝试刷新成员列表", m.Sender.Uin) - t, err := bot.Client.GetGroupMembers(group) - if err != nil { - log.Warnf("刷新群 %v 成员列表失败: %v", group.Uin, err) - return nil - } - group.Members = t - mem = group.FindMember(m.Sender.Uin) - if mem == nil { - return nil - } - } - ms := gm["sender"].(global.MSG) - switch mem.Permission { - case client.Owner: - ms["role"] = "owner" - case client.Administrator: - ms["role"] = "admin" - case client.Member: - ms["role"] = "member" - default: - ms["role"] = "member" - } - ms["nickname"] = mem.Nickname - ms["card"] = mem.CardName - ms["title"] = mem.SpecialTitle - } - return gm -} - func formatGroupName(group *client.GroupInfo) string { return fmt.Sprintf("%s(%d)", group.Name, group.Code) } diff --git a/coolq/converter.go b/coolq/converter.go index 44b1e14..7e72249 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -2,7 +2,9 @@ package coolq import ( "github.com/Mrs4s/MiraiGo/client" + "github.com/Mrs4s/MiraiGo/message" "github.com/Mrs4s/go-cqhttp/global" + log "github.com/sirupsen/logrus" "strconv" ) @@ -55,6 +57,80 @@ func convertGuildMemberInfo(m *client.GuildMemberInfo) global.MSG { } } +func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { + source := MessageSource{ + SourceType: MessageSourceGroup, + PrimaryID: uint64(m.GroupCode), + } + cqm := ToStringMessage(m.Elements, source, true) + gm := global.MSG{ + "anonymous": nil, + "font": 0, + "group_id": m.GroupCode, + "message": ToFormattedMessage(m.Elements, source, false), + "message_type": "group", + "message_seq": m.Id, + "post_type": func() string { + if m.Sender.Uin == bot.Client.Uin { + return "message_sent" + } + return "message" + }(), + "raw_message": cqm, + "self_id": bot.Client.Uin, + "sender": global.MSG{ + "age": 0, + "area": "", + "level": "", + "sex": "unknown", + "user_id": m.Sender.Uin, + }, + "sub_type": "normal", + "time": m.Time, + "user_id": m.Sender.Uin, + } + if m.Sender.IsAnonymous() { + gm["anonymous"] = global.MSG{ + "flag": m.Sender.AnonymousInfo.AnonymousId + "|" + m.Sender.AnonymousInfo.AnonymousNick, + "id": m.Sender.Uin, + "name": m.Sender.AnonymousInfo.AnonymousNick, + } + gm["sender"].(global.MSG)["nickname"] = "匿名消息" + gm["sub_type"] = "anonymous" + } else { + group := bot.Client.FindGroup(m.GroupCode) + mem := group.FindMember(m.Sender.Uin) + if mem == nil { + log.Warnf("获取 %v 成员信息失败,尝试刷新成员列表", m.Sender.Uin) + t, err := bot.Client.GetGroupMembers(group) + if err != nil { + log.Warnf("刷新群 %v 成员列表失败: %v", group.Uin, err) + return nil + } + group.Members = t + mem = group.FindMember(m.Sender.Uin) + if mem == nil { + return nil + } + } + ms := gm["sender"].(global.MSG) + switch mem.Permission { + case client.Owner: + ms["role"] = "owner" + case client.Administrator: + ms["role"] = "admin" + case client.Member: + ms["role"] = "member" + default: + ms["role"] = "member" + } + ms["nickname"] = mem.Nickname + ms["card"] = mem.CardName + ms["title"] = mem.SpecialTitle + } + return gm +} + func convertChannelInfo(c *client.ChannelInfo) global.MSG { slowModes := make([]global.MSG, 0, len(c.Meta.SlowModes)) for _, mode := range c.Meta.SlowModes { diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 14feeea..5d436ff 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -36,11 +36,6 @@ var typeReg = regexp.MustCompile(`\[CQ:(\w+)`) var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`) */ -const ( - maxImageSize = 1024 * 1024 * 30 // 30MB - maxVideoSize = 1024 * 1024 * 100 // 100MB -) - // PokeElement 拍一拍 type PokeElement struct { Target int64 @@ -73,6 +68,27 @@ type LocalVideoElement struct { thumb io.ReadSeeker } +// MessageSource 消息来源 +// 如果为私聊或者群聊, PrimaryID 将代表群号/QQ号 +// 如果为频道, PrimaryID 为 GuildID, SubID 为 ChannelID +type MessageSource struct { + SourceType MessageSourceType + PrimaryID uint64 + SubID uint64 +} + +// MessageSourceType 消息来源类型 +type MessageSourceType int32 + +const ( + maxImageSize = 1024 * 1024 * 30 // 30MB + maxVideoSize = 1024 * 1024 * 100 // 100MB + + MessageSourcePrivate MessageSourceType = 0 + MessageSourceGroup MessageSourceType = 1 + MessageSourceGuildChannel MessageSourceType = 2 +) + // Type implements the message.IMessageElement. func (e *LocalImageElement) Type() message.ElementType { return message.Image @@ -114,16 +130,16 @@ func (e *PokeElement) Type() message.ElementType { } // ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报 -func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG) { +func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []global.MSG) { r = make([]global.MSG, 0, len(e)) m := &message.SendingMessage{Elements: e} reply := m.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) return ok }) - if reply != nil { + if reply != nil && source.SourceType == MessageSourceGroup { replyElem := reply.(*message.ReplyElement) - rid := groupID + rid := int64(source.PrimaryID) if rid == 0 { rid = replyElem.Sender } @@ -138,7 +154,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG) "seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10), "qq": strconv.FormatInt(replyElem.Sender, 10), "time": strconv.FormatInt(int64(replyElem.Time), 10), - "text": ToStringMessage(replyElem.Elements, groupID), + "text": ToStringMessage(replyElem.Elements, source), }, }) } else { @@ -260,7 +276,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG) } // ToStringMessage 将消息元素数组转为字符串以用于消息上报 -func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r string) { +func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw ...bool) (r string) { sb := global.NewBuffer() sb.Reset() write := func(format string, a ...interface{}) { @@ -276,9 +292,9 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) _, ok := e.(*message.ReplyElement) return ok }) - if reply != nil { + if reply != nil && source.SourceType == MessageSourceGroup { replyElem := reply.(*message.ReplyElement) - rid := groupID + rid := int64(source.PrimaryID) if rid == 0 { rid = replyElem.Sender } @@ -289,7 +305,7 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]", db.ToGlobalID(rid, replyElem.ReplySeq), replyElem.ReplySeq, replyElem.Sender, replyElem.Time, - CQCodeEscapeValue(ToStringMessage(replyElem.Elements, groupID))) + CQCodeEscapeValue(ToStringMessage(replyElem.Elements, source))) } else { write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq)) } @@ -352,6 +368,8 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) } else { write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url), arg) } + case *message.GuildImageElement: + write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url)) case *message.DiceElement: write("[CQ:dice,value=%v]", o.Value) case *message.MarketFaceElement: @@ -480,7 +498,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) { } // ConvertStringMessage 将消息字符串转为消息元素数组 -func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IMessageElement) { +func (bot *CQBot) ConvertStringMessage(raw string, sourceType MessageSourceType) (r []message.IMessageElement) { var t, key string d := map[string]string{} @@ -516,7 +534,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM ReplySeq: org.GetAttribute().MessageSeq, Sender: org.GetAttribute().SenderUin, Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertStringMessage(customText, isGroup), + Elements: bot.ConvertStringMessage(customText, sourceType), } if senderErr != nil { elem.Sender = sender @@ -532,7 +550,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM ReplySeq: int32(messageSeq), Sender: sender, Time: int32(msgTime), - Elements: bot.ConvertStringMessage(customText, isGroup), + Elements: bot.ConvertStringMessage(customText, sourceType), } } r = append([]message.IMessageElement{elem}, r...) @@ -544,7 +562,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM ReplySeq: org.GetAttribute().MessageSeq, Sender: org.GetAttribute().SenderUin, Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertContentMessage(org.GetContent(), isGroup), + Elements: bot.ConvertContentMessage(org.GetContent(), sourceType), }, }, r...) } @@ -565,7 +583,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM } return } - elem, err := bot.ToElement(t, d, isGroup) + elem, err := bot.ToElement(t, d, sourceType) if err != nil { org := "[CQ:" + t for k, v := range d { @@ -653,11 +671,11 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM } // ConvertObjectMessage 将消息JSON对象转为消息元素数组 -func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []message.IMessageElement) { +func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType MessageSourceType) (r []message.IMessageElement) { d := make(map[string]string) convertElem := func(e gjson.Result) { t := e.Get("type").Str - if t == "reply" && isGroup { + if t == "reply" && sourceType == MessageSourceGroup { if len(r) > 0 { if _, ok := r[0].(*message.ReplyElement); ok { log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") @@ -688,7 +706,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag ReplySeq: org.GetAttribute().MessageSeq, Sender: org.GetAttribute().SenderUin, Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertStringMessage(customText, isGroup), + Elements: bot.ConvertStringMessage(customText, sourceType), } if senderErr != nil { elem.Sender = sender @@ -704,7 +722,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag ReplySeq: int32(messageSeq), Sender: sender, Time: int32(msgTime), - Elements: bot.ConvertStringMessage(customText, isGroup), + Elements: bot.ConvertStringMessage(customText, sourceType), } } r = append([]message.IMessageElement{elem}, r...) @@ -716,7 +734,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag ReplySeq: org.GetAttribute().MessageSeq, Sender: org.GetAttribute().SenderUin, Time: int32(org.GetAttribute().Timestamp), - Elements: bot.ConvertContentMessage(org.GetContent(), isGroup), + Elements: bot.ConvertContentMessage(org.GetContent(), sourceType), }, }, r...) } @@ -745,7 +763,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag d[key.Str] = value.String() return true }) - elem, err := bot.ToElement(t, d, isGroup) + elem, err := bot.ToElement(t, d, sourceType) if err != nil { log.Warnf("转换CQ码 (%v) 到MiraiGo Element时出现错误: %v 将忽略本段CQ码.", e.Raw, err) return @@ -758,7 +776,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag } } if m.Type == gjson.String { - return bot.ConvertStringMessage(m.Str, isGroup) + return bot.ConvertStringMessage(m.Str, sourceType) } if m.IsArray() { m.ForEach(func(_, e gjson.Result) bool { @@ -773,14 +791,14 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag } // ConvertContentMessage 将数据库用的 content 转换为消息元素数组 -func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []message.IMessageElement) { +func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType MessageSourceType) (r []message.IMessageElement) { for _, c := range content { data := c["data"].(global.MSG) switch c["type"] { case "text": r = append(r, message.NewText(data["text"].(string))) case "image": - e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, false, group) + e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, false, sourceType) if err != nil { log.Warnf("make image elem error: %v", err) continue @@ -830,7 +848,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []m case "face": r = append(r, message.NewFace(data["id"].(int32))) case "video": - e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, group) + e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, sourceType) if err != nil { log.Warnf("make image elem error: %v", err) continue @@ -846,7 +864,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []m // 返回 interface{} 存在三种类型 // // message.IMessageElement []message.IMessageElement nil -func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m interface{}, err error) { +func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSourceType) (m interface{}, err error) { switch t { case "text": if base.SplitURL { @@ -858,7 +876,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte } return message.NewText(d["text"]), nil case "image": - img, err := bot.makeImageOrVideoElem(d, false, isGroup) + img, err := bot.makeImageOrVideoElem(d, false, sourceType) if err != nil { return nil, err } @@ -892,7 +910,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte t, _ := strconv.ParseInt(d["qq"], 10, 64) return &PokeElement{Target: t}, nil case "gift": - if !isGroup { + if sourceType != MessageSourceGroup { return nil, errors.New("private gift unsupported") // no free private gift } t, _ := strconv.ParseInt(d["qq"], 10, 64) @@ -1088,13 +1106,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte maxWidth := parseIntWithDefault("maxwidth", 500) minHeight := parseIntWithDefault("minheight", 200) maxHeight := parseIntWithDefault("maxheight", 1000) - img, err := bot.makeImageOrVideoElem(d, false, isGroup) + img, err := bot.makeImageOrVideoElem(d, false, sourceType) if err != nil { return nil, errors.New("send cardimage faild") } - return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, isGroup) + return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == MessageSourceGroup) case "video": - file, err := bot.makeImageOrVideoElem(d, true, isGroup) + file, err := bot.makeImageOrVideoElem(d, true, sourceType) if err != nil { return nil, err } @@ -1248,7 +1266,7 @@ func CQCodeUnescapeValue(content string) string { } // makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用 -func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (message.IMessageElement, error) { +func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType MessageSourceType) (message.IMessageElement, error) { f := d["file"] if strings.HasPrefix(f, "http") { hash := md5.Sum([]byte(f)) @@ -1327,11 +1345,18 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) ( b, _ := os.ReadFile(rawPath) return bot.readVideoCache(b), nil } + // 目前频道内上传的图片均无法被查询到, 需要单独处理 + if sourceType == MessageSourceGuildChannel { + cacheFile := path.Join(global.ImagePath, "guild-images", f) + if global.PathExists(cacheFile) { + return &LocalImageElement{File: cacheFile}, nil + } + } if strings.HasSuffix(f, ".image") && cache.EnableCacheDB { hash, err := hex.DecodeString(strings.TrimSuffix(f, ".image")) if err == nil { if b := cache.Image.Get(hash); b != nil { - return bot.readImageCache(b, group) + return bot.readImageCache(b, sourceType) } } } @@ -1342,7 +1367,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) ( } if !exist { if d["url"] != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group) + return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, sourceType) } return nil, errors.New("invalid image") } @@ -1353,10 +1378,10 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) ( if err != nil { return nil, err } - return bot.readImageCache(b, group) + return bot.readImageCache(b, sourceType) } -func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement, error) { +func (bot *CQBot) readImageCache(b []byte, sourceType MessageSourceType) (message.IMessageElement, error) { var err error if len(b) < 20 { return nil, errors.New("invalid cache") @@ -1368,7 +1393,7 @@ func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement, imageURL := r.ReadString() if size == 0 { if imageURL != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group) + return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) } return nil, errors.New("img size is 0") } @@ -1376,15 +1401,23 @@ func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement, return nil, errors.New("invalid hash") } var rsp message.IMessageElement - if group { + if sourceType == MessageSourceGroup { rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size) goto ok } + if sourceType == MessageSourceGuildChannel { + if len(bot.Client.GuildService.Guilds) == 0 { + err = errors.New("cannot query guild image: not any joined guild") + } + guild := bot.Client.GuildService.Guilds[0] + rsp, err = bot.Client.GuildService.QueryImage(guild.GuildId, guild.Channels[0].ChannelId, hash, uint64(size)) + goto ok + } rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size) ok: if err != nil { if imageURL != "" { - return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group) + return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType) } return nil, err } diff --git a/coolq/event.go b/coolq/event.go index a9f4829..5e574e5 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -22,18 +22,22 @@ import ( ) // ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式 -func ToFormattedMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r interface{}) { +func ToFormattedMessage(e []message.IMessageElement, source MessageSource, isRaw ...bool) (r interface{}) { if base.PostFormat == "string" { - r = ToStringMessage(e, groupID, isRaw...) + r = ToStringMessage(e, source, isRaw...) } else if base.PostFormat == "array" { - r = ToArrayMessage(e, groupID) + r = ToArrayMessage(e, source) } return } func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) { bot.checkMedia(m.Elements, m.Sender.Uin) - cqm := ToStringMessage(m.Elements, 0, true) + source := MessageSource{ + SourceType: MessageSourcePrivate, + PrimaryID: uint64(m.Sender.Uin), + } + cqm := ToStringMessage(m.Elements, source, true) id := bot.InsertPrivateMessage(m) log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id) fm := global.MSG{ @@ -48,7 +52,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess "message_id": id, "user_id": m.Sender.Uin, "target_id": m.Target, - "message": ToFormattedMessage(m.Elements, 0, false), + "message": ToFormattedMessage(m.Elements, source, false), "raw_message": cqm, "font": 0, "self_id": c.Uin, @@ -86,7 +90,11 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) return } } - cqm := ToStringMessage(m.Elements, m.GroupCode, true) + source := MessageSource{ + SourceType: MessageSourceGroup, + PrimaryID: uint64(m.GroupCode), + } + cqm := ToStringMessage(m.Elements, source, true) id := bot.InsertGroupMessage(m) log.Infof("收到群 %v(%v) 内 %v(%v) 的消息: %v (%v)", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm, id) gm := bot.formatGroupMessage(m) @@ -100,7 +108,11 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage) func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) { m := e.Message bot.checkMedia(m.Elements, m.Sender.Uin) - cqm := ToStringMessage(m.Elements, 0, true) + source := MessageSource{ + SourceType: MessageSourcePrivate, + PrimaryID: uint64(e.Session.Sender), + } + cqm := ToStringMessage(m.Elements, source, true) bot.tempSessionCache.Store(m.Sender.Uin, e.Session) id := m.Id // todo(Mrs4s) @@ -115,7 +127,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven "temp_source": e.Session.Source, "message_id": id, "user_id": m.Sender.Uin, - "message": ToFormattedMessage(m.Elements, 0, false), + "message": ToFormattedMessage(m.Elements, source, false), "raw_message": cqm, "font": 0, "self_id": c.Uin, @@ -143,7 +155,12 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC channel = c } } - log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, 0, true)) + source := MessageSource{ + SourceType: MessageSourceGuildChannel, + PrimaryID: m.GuildId, + SubID: m.ChannelId, + } + log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, source, true)) // todo: 数据库支持 bot.dispatchEventMessage(global.MSG{ "post_type": "message", @@ -153,7 +170,7 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC "channel_id": m.ChannelId, "message_id": fmt.Sprintf("%v-%v", m.Id, m.InternalId), "user_id": m.Sender.TinyId, - "message": ToFormattedMessage(m.Elements, 0, false), // todo: 增加对频道消息 Reply 的支持 + "message": ToFormattedMessage(m.Elements, source, false), // todo: 增加对频道消息 Reply 的支持 "self_id": bot.Client.Uin, "self_tiny_id": bot.Client.GuildService.TinyId, "time": m.Time, @@ -665,6 +682,12 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) { } else if !global.PathExists(path.Join(global.ImagePath, filename)) { _ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644) } + + if i.Url != "" && !global.PathExists(path.Join(global.ImagePath, "guild-images", filename)) { + if err := global.DownloadFile(i.Url, path.Join(global.ImagePath, "guild-images", filename), -1, map[string]string{}); err != nil { + log.Warnf("下载频道图片时出现错误: %v", err) + } + } case *message.FriendImageElement: data := binary.NewWriterF(func(w *binary.Writer) { w.Write(i.Md5) diff --git a/go.mod b/go.mod index 96af2ab..f98ecd1 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2 require ( github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Microsoft/go-winio v0.5.1 - github.com/Mrs4s/MiraiGo v0.0.0-20211111181749-b85fc25cd59b + github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02 github.com/dustin/go-humanize v1.0.0 github.com/fumiama/go-hide-param v0.1.4 github.com/gabriel-vasile/mimetype v1.4.0 diff --git a/go.sum b/go.sum index 9cf1be2..737922e 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,8 @@ github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/g github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Mrs4s/MiraiGo v0.0.0-20211111181749-b85fc25cd59b h1:tu/xKD248MLJL63zH/gJttk0u3t4aaMV+GNsU5EmIEc= -github.com/Mrs4s/MiraiGo v0.0.0-20211111181749-b85fc25cd59b/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= +github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02 h1:D2a93R7eugdAfuZtVXYznjqGrKlfvtWQ0YTHnwJYq8U= +github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02/go.mod h1:UOO08UBb5mVisfwrjduINu5GF9+gmsn33f2tnr83xnE= github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index 6c80b78..cbff141 100644 --- a/main.go +++ b/main.go @@ -84,6 +84,7 @@ func main() { mkCacheDir(global.VoicePath, "语音") mkCacheDir(global.VideoPath, "视频") mkCacheDir(global.CachePath, "发送图片") + mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存") cache.Init() db.Init()