diff --git a/coolq/api.go b/coolq/api.go index 23b3285..190484d 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -1577,7 +1577,7 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG { // @route(get_guild_msg) func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { source, seq := decodeGuildMessageID(messageID) - if source == nil { + if source.SourceType == 0 { log.Warnf("获取消息时出现错误: 无效消息ID") return Failed(100, "INVALID_MESSAGE_ID", "无效消息ID") } @@ -1613,7 +1613,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(pull[0].Sender.TinyId), "nickname": pull[0].Sender.Nickname, } - m["message"] = ToFormattedMessage(pull[0].Elements, *source, false) + m["message"] = ToFormattedMessage(pull[0].Elements, source, false) m["reactions"] = convertReactions(pull[0].Reactions) bot.InsertGuildChannelMessage(pull[0]) } else { @@ -1628,7 +1628,7 @@ func (bot *CQBot) CQGetGuildMessage(messageID string, noCache bool) global.MSG { "tiny_id": fU64(channelMsgByDB.Attribute.SenderTinyID), "nickname": channelMsgByDB.Attribute.SenderName, } - m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), *source) + m["message"] = ToFormattedMessage(bot.ConvertContentMessage(channelMsgByDB.Content, message.SourceGuildChannel), source, false) } case message.SourceGuildDirect: // todo(mrs4s): 支持 direct 消息 diff --git a/coolq/bot.go b/coolq/bot.go index aea4845..e6507cd 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -630,13 +630,13 @@ func encodeGuildMessageID(primaryID, subID, seq uint64, source message.SourceTyp })) } -func decodeGuildMessageID(id string) (source *message.Source, seq uint64) { +func decodeGuildMessageID(id string) (source message.Source, seq uint64) { b, _ := base64.StdEncoding.DecodeString(id) if len(b) < 25 { return } r := binary.NewReader(b) - source = &message.Source{ + source = message.Source{ SourceType: message.SourceType(r.ReadByte()), PrimaryID: r.ReadInt64(), SecondaryID: r.ReadInt64(), diff --git a/coolq/converter.go b/coolq/converter.go index 43be08e..65f26c4 100644 --- a/coolq/converter.go +++ b/coolq/converter.go @@ -2,6 +2,7 @@ package coolq import ( "strconv" + "strings" "github.com/Mrs4s/MiraiGo/topic" @@ -64,7 +65,7 @@ func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG { SourceType: message.SourceGroup, PrimaryID: m.GroupCode, } - cqm := ToStringMessage(m.Elements, source, true) + cqm := toStringMessage(m.Elements, source, true) postType := "message" if m.Sender.Uin == bot.Client.Uin { postType = "message_sent" @@ -211,6 +212,15 @@ func convertReactions(reactions []*message.GuildMessageEmojiReaction) (r []globa return } +func toStringMessage(m []message.IMessageElement, source message.Source, raw bool) string { + elems := toElements(m, source, raw) + var sb strings.Builder + for _, elem := range elems { + sb.WriteString(elem.CQCode()) + } + return sb.String() +} + func fU64(v uint64) string { return strconv.FormatUint(v, 10) } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 8ea7aac..d97c63d 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -92,9 +92,12 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { return db.ToGlobalID(id, seq) } -// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报 -func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []global.MSG) { - r = make([]global.MSG, 0, len(e)) +// toElements 将消息元素数组转为MSG数组以用于消息上报 +func toElements(e []message.IMessageElement, source message.Source, raw bool) (r []cqcode.Element) { + type pair = cqcode.Pair // simplify code + type pairs = []pair + + r = make([]cqcode.Element, 0, len(e)) m := &message.SendingMessage{Elements: e} reply := m.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) @@ -103,26 +106,24 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) id := replyID(replyElem, source) - if base.ExtraReplyData { - r = append(r, global.MSG{ - "type": "reply", - "data": map[string]string{ - "id": strconv.FormatInt(int64(id), 10), - "seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10), - "qq": strconv.FormatInt(replyElem.Sender, 10), - "time": strconv.FormatInt(int64(replyElem.Time), 10), - "text": ToStringMessage(replyElem.Elements, source), - }, - }) - } else { - r = append(r, global.MSG{ - "type": "reply", - "data": map[string]string{"id": strconv.FormatInt(int64(id), 10)}, - }) + elem := cqcode.Element{ + Type: "reply", + Data: pairs{ + {"id", strconv.FormatInt(int64(id), 10)}, + }, } + if base.ExtraReplyData { + elem.Data = append(elem.Data, + pair{K: "seq", V: strconv.FormatInt(int64(replyElem.ReplySeq), 10)}, + pair{K: "qq", V: strconv.FormatInt(replyElem.Sender, 10)}, + pair{K: "time", V: strconv.FormatInt(int64(replyElem.Time), 10)}, + pair{K: "text", V: toStringMessage(replyElem.Elements, source, true)}, + ) + } + r = append(r, elem) } for i, elem := range e { - var m global.MSG + var m cqcode.Element switch o := elem.(type) { case *message.ReplyElement: if base.RemoveReplyAt && i+1 < len(e) { @@ -133,103 +134,143 @@ func ToArrayMessage(e []message.IMessageElement, source message.Source) (r []glo } continue case *message.TextElement: - m = global.MSG{ - "type": "text", - "data": map[string]string{"text": o.Content}, + m = cqcode.Element{ + Type: "text", + Data: pairs{ + {"text", o.Content}, + }, } case *message.LightAppElement: - m = global.MSG{ - "type": "json", - "data": map[string]string{"data": o.Content}, + m = cqcode.Element{ + Type: "json", + Data: pairs{ + {"data", o.Content}, + }, } case *message.AtElement: target := "all" if o.Target != 0 { target = strconv.FormatUint(uint64(o.Target), 10) } - m = global.MSG{ - "type": "at", - "data": map[string]string{"qq": target}, + m = cqcode.Element{ + Type: "at", + Data: pairs{ + {"qq", target}, + }, } case *message.RedBagElement: - m = global.MSG{ - "type": "redbag", - "data": map[string]string{"title": o.Title}, + m = cqcode.Element{ + Type: "redbag", + Data: pairs{ + {"title", o.Title}, + }, } case *message.ForwardElement: - m = global.MSG{ - "type": "forward", - "data": map[string]string{"id": o.ResId}, + m = cqcode.Element{ + Type: "forward", + Data: pairs{ + {"id", o.ResId}, + }, } case *message.FaceElement: - m = global.MSG{ - "type": "face", - "data": map[string]string{"id": strconv.FormatInt(int64(o.Index), 10)}, + m = cqcode.Element{ + Type: "face", + Data: pairs{ + {"id", strconv.FormatInt(int64(o.Index), 10)}, + }, } case *message.VoiceElement: - m = global.MSG{ - "type": "record", - "data": map[string]string{"file": o.Name, "url": o.Url}, + m = cqcode.Element{ + Type: "record", + Data: pairs{ + {"file", o.Name}, + {"url", o.Url}, + }, } case *message.ShortVideoElement: - m = global.MSG{ - "type": "video", - "data": map[string]string{"file": o.Name, "url": o.Url}, + m = cqcode.Element{ + Type: "video", + Data: pairs{ + {"file", o.Name}, + {"url", o.Url}, + }, } case *message.GroupImageElement: - data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url, "subType": strconv.FormatInt(int64(o.ImageBizType), 10)} + data := pairs{ + {"file", hex.EncodeToString(o.Md5) + ".image"}, + {"subType", strconv.FormatInt(int64(o.ImageBizType), 10)}, + } + if raw { + data = append(data, pair{K: "url", V: o.Url}) + } switch { case o.Flash: - data["type"] = "flash" + data = append(data, pair{K: "type", V: "flash"}) case o.EffectID != 0: - data["type"] = "show" - data["id"] = strconv.FormatInt(int64(o.EffectID), 10) + data = append(data, pair{K: "type", V: "show"}) + data = append(data, pair{K: "id", V: strconv.FormatInt(int64(o.EffectID), 10)}) } - m = global.MSG{ - "type": "image", - "data": data, + m = cqcode.Element{ + Type: "image", + Data: data, } case *message.GuildImageElement: - data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url} - m = global.MSG{ - "type": "image", - "data": data, + data := pairs{ + {"file", hex.EncodeToString(o.Md5) + ".image"}, + } + if raw { + data = append(data, pair{K: "url", V: o.Url}) + } + m = cqcode.Element{ + Type: "image", + Data: data, } case *message.FriendImageElement: - data := map[string]string{"file": hex.EncodeToString(o.Md5) + ".image", "url": o.Url} - if o.Flash { - data["type"] = "flash" + data := pairs{ + {"file", hex.EncodeToString(o.Md5) + ".image"}, } - m = global.MSG{ - "type": "image", - "data": data, + if raw { + data = append(data, pair{K: "url", V: o.Url}) + } + if o.Flash { + data = append(data, pair{K: "type", V: "flash"}) + } + m = cqcode.Element{ + Type: "image", + Data: data, } case *message.DiceElement: - m = global.MSG{ - "type": "dice", - "data": map[string]string{"value": strconv.FormatInt(int64(o.Value), 10)}, + m = cqcode.Element{ + Type: "dice", + Data: pairs{ + {"value", strconv.FormatInt(int64(o.Value), 10)}, + }, } case *message.MarketFaceElement: - m = global.MSG{ - "type": "text", - "data": map[string]string{"text": o.Name}, + m = cqcode.Element{ + Type: "text", + Data: pairs{ + {"text", o.Name}, + }, } case *message.ServiceElement: - if isOk := strings.Contains(o.Content, " i+1 { - elem, ok := e[i+1].(*message.AtElement) - if ok && elem.Target == o.Sender { - e[i+1] = nil - } - } - case *message.TextElement: - sb.WriteString(cqcode.EscapeText(o.Content)) - case *message.AtElement: - if o.Target == 0 { - write("[CQ:at,qq=all]") - continue - } - write("[CQ:at,qq=%d]", uint64(o.Target)) - case *message.RedBagElement: - write("[CQ:redbag,title=%s]", o.Title) - case *message.ForwardElement: - write("[CQ:forward,id=%s]", o.ResId) - case *message.FaceElement: - write(`[CQ:face,id=%d]`, o.Index) - case *message.VoiceElement: - if ur { - write(`[CQ:record,file=%s]`, o.Name) - } else { - write(`[CQ:record,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url)) - } - case *message.ShortVideoElement: - if ur { - write(`[CQ:video,file=%s]`, o.Name) - } else { - write(`[CQ:video,file=%s,url=%s]`, o.Name, cqcode.EscapeValue(o.Url)) - } - case *message.GroupImageElement: - var arg string - if o.Flash { - arg = ",type=flash" - } else if o.EffectID != 0 { - arg = ",type=show,id=" + strconv.FormatInt(int64(o.EffectID), 10) - } - arg += ",subType=" + strconv.FormatInt(int64(o.ImageBizType), 10) - if ur { - write("[CQ:image,file=%x.image%s]", o.Md5, arg) - } else { - write("[CQ:image,file=%x.image,url=%s%s]", o.Md5, cqcode.EscapeValue(o.Url), arg) - } - case *message.FriendImageElement: - var arg string - if o.Flash { - arg = ",type=flash" - } - if ur { - write("[CQ:image,file=%x.image%s]", o.Md5, arg) - } else { - write("[CQ:image,file=%x.image,url=%s%s]", cqcode.EscapeValue(o.Url), arg) - } - case *LocalImageElement: - var arg string - if o.Flash { - arg = ",type=flash" - } - data, err := os.ReadFile(o.File) - if err == nil { - m := md5.Sum(data) - if ur { - write("[CQ:image,file=%x.image%s]", m[:], arg) - } else { - write("[CQ:image,file=%x.image,url=%s%s]", m[:], cqcode.EscapeValue(o.URL), arg) - } - } - case *message.GuildImageElement: - write("[CQ:image,file=%x.image,url=%s]", o.Md5, cqcode.EscapeValue(o.Url)) - case *message.DiceElement: - write("[CQ:dice,value=%v]", o.Value) - case *message.MarketFaceElement: - sb.WriteString(o.Name) - case *message.ServiceElement: - if isOk := strings.Contains(o.Content, "