From 43dd9aa76db6671a14f91ac5088270eaae76a0ae Mon Sep 17 00:00:00 2001 From: wdvxdr Date: Wed, 15 Feb 2023 22:29:42 +0800 Subject: [PATCH] rf: move coolq/cqcode to internal/msg --- cmd/api-generator/main.go | 2 +- coolq/api.go | 5 +- coolq/bot.go | 19 +-- coolq/cqcode.go | 140 ++++++------------ coolq/cqcode/escape.go | 79 ---------- coolq/cqcode_test.go | 6 +- {coolq/cqcode => internal/msg}/element.go | 82 +++++++++- .../msg/element_test.go | 2 +- internal/msg/local.go | 44 ++++++ internal/onebot/spec.go | 3 +- modules/api/caller.go | 2 +- 11 files changed, 193 insertions(+), 191 deletions(-) delete mode 100644 coolq/cqcode/escape.go rename {coolq/cqcode => internal/msg}/element.go (63%) rename coolq/cqcode/all_test.go => internal/msg/element_test.go (96%) create mode 100644 internal/msg/local.go diff --git a/cmd/api-generator/main.go b/cmd/api-generator/main.go index 031e493..95260b7 100644 --- a/cmd/api-generator/main.go +++ b/cmd/api-generator/main.go @@ -177,7 +177,7 @@ func conv(v, t string) string { switch t { default: panic("unknown type: " + t) - case "gjson.Result", "*onebot.Spec", "IDConverter": + case "gjson.Result", "*onebot.Spec": return v case "int64": return v + ".Int()" diff --git a/coolq/api.go b/coolq/api.go index e08bada..345c840 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -27,6 +27,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/download" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/Mrs4s/go-cqhttp/internal/onebot" "github.com/Mrs4s/go-cqhttp/internal/param" "github.com/Mrs4s/go-cqhttp/modules/filter" @@ -851,7 +852,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType for i, elem := range elems { p := &elems[i] switch o := elem.(type) { - case *LocalVideoElement: + case *msg.LocalVideo: w.do(func() { gm, err := bot.uploadLocalVideo(source, o) if err != nil { @@ -860,7 +861,7 @@ func (bot *CQBot) uploadForwardElement(m gjson.Result, target int64, sourceType *p = gm } }) - case *LocalImageElement: + case *msg.LocalImage: w.do(func() { gm, err := bot.uploadLocalImage(source, o) if err != nil { diff --git a/coolq/bot.go b/coolq/bot.go index 89cf8d1..268c28b 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -26,6 +26,7 @@ import ( "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/mime" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/Mrs4s/go-cqhttp/internal/onebot" ) @@ -147,7 +148,7 @@ func (w *worker) wait() { } // uploadLocalImage 上传本地图片 -func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement) (message.IMessageElement, error) { +func (bot *CQBot) uploadLocalImage(target message.Source, img *msg.LocalImage) (message.IMessageElement, error) { if img.File != "" { f, err := os.Open(img.File) if err != nil { @@ -187,20 +188,20 @@ func (bot *CQBot) uploadLocalImage(target message.Source, img *LocalImageElement } // uploadLocalVideo 上传本地短视频至群聊 -func (bot *CQBot) uploadLocalVideo(target message.Source, v *LocalVideoElement) (*message.ShortVideoElement, error) { +func (bot *CQBot) uploadLocalVideo(target message.Source, v *msg.LocalVideo) (*message.ShortVideoElement, error) { video, err := os.Open(v.File) if err != nil { return nil, err } defer func() { _ = video.Close() }() - return bot.Client.UploadShortVideo(target, video, v.thumb, 4) + return bot.Client.UploadShortVideo(target, video, v.Thumb, 4) } func removeLocalElement(elements []message.IMessageElement) []message.IMessageElement { var j int for i, e := range elements { switch e.(type) { - case *LocalImageElement, *LocalVideoElement: + case *msg.LocalImage, *msg.LocalVideo: case *message.VoiceElement: // 未上传的语音消息, 也删除 case nil: default: @@ -230,7 +231,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage for i, m := range elements { p := &elements[i] switch e := m.(type) { - case *LocalImageElement: + case *msg.LocalImage: w.do(func() { m, err := bot.uploadLocalImage(target, e) if err != nil { @@ -248,7 +249,7 @@ func (bot *CQBot) uploadMedia(target message.Source, elements []message.IMessage *p = m } }) - case *LocalVideoElement: + case *msg.LocalVideo: w.do(func() { m, err := bot.uploadLocalVideo(target, e) if err != nil { @@ -274,7 +275,7 @@ func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) (in m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *PokeElement: + case *msg.Poke: if group != nil { if mem := group.FindMember(i.Target); mem != nil { mem.Poke() @@ -319,7 +320,7 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen m.Elements = bot.uploadMedia(source, m.Elements) for _, e := range m.Elements { switch i := e.(type) { - case *PokeElement: + case *msg.Poke: bot.Client.SendFriendPoke(i.Target) return 0 case *message.MusicShareElement: @@ -421,7 +422,7 @@ func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message. bot.Client.SendGuildMusicShare(guildID, channelID, i) return "-1" // todo: fix this - case *message.VoiceElement, *PokeElement: + case *message.VoiceElement, *msg.Poke: log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String()) continue } diff --git a/coolq/cqcode.go b/coolq/cqcode.go index 705fab4..9d65ebb 100644 --- a/coolq/cqcode.go +++ b/coolq/cqcode.go @@ -25,64 +25,24 @@ import ( log "github.com/sirupsen/logrus" "github.com/tidwall/gjson" - "github.com/Mrs4s/go-cqhttp/coolq/cqcode" "github.com/Mrs4s/go-cqhttp/db" "github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/internal/base" "github.com/Mrs4s/go-cqhttp/internal/cache" "github.com/Mrs4s/go-cqhttp/internal/download" "github.com/Mrs4s/go-cqhttp/internal/mime" + "github.com/Mrs4s/go-cqhttp/internal/msg" "github.com/Mrs4s/go-cqhttp/internal/param" ) -/* -var matchReg = regexp.MustCompile(`\[CQ:\w+?.*?]`) -var typeReg = regexp.MustCompile(`\[CQ:(\w+)`) -var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`) -*/ - -// PokeElement 拍一拍 -type PokeElement struct { - Target int64 -} - -// LocalImageElement 本地图片 -type LocalImageElement struct { - Stream io.ReadSeeker - File string - URL string - - Flash bool - EffectID int32 -} - -// LocalVideoElement 本地视频 -type LocalVideoElement struct { - File string - thumb io.ReadSeeker -} +// TODO: move this file to internal/msg, internal/onebot +// TODO: support OneBot V12 const ( maxImageSize = 1024 * 1024 * 30 // 30MB maxVideoSize = 1024 * 1024 * 100 // 100MB ) -// Type implements the message.IMessageElement. -func (e *LocalImageElement) Type() message.ElementType { - return message.Image -} - -// Type impl message.IMessageElement -func (e *LocalVideoElement) Type() message.ElementType { - return message.Video -} - -// Type 获取元素类型ID -func (e *PokeElement) Type() message.ElementType { - // Make message.IMessageElement Happy - return message.At -} - func replyID(r *message.ReplyElement, source message.Source) int32 { id := source.PrimaryID seq := r.ReplySeq @@ -102,11 +62,11 @@ func replyID(r *message.ReplyElement, source message.Source) int32 { // toElements 将消息元素数组转为MSG数组以用于消息上报 // // nolint:govet -func toElements(e []message.IMessageElement, source message.Source) (r []cqcode.Element) { - type pair = cqcode.Pair // simplify code +func toElements(e []message.IMessageElement, source message.Source) (r []msg.Element) { + type pair = msg.Pair // simplify code type pairs = []pair - r = make([]cqcode.Element, 0, len(e)) + r = make([]msg.Element, 0, len(e)) m := &message.SendingMessage{Elements: e} reply := m.FirstOrNil(func(e message.IMessageElement) bool { _, ok := e.(*message.ReplyElement) @@ -115,7 +75,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if reply != nil && source.SourceType&(message.SourceGroup|message.SourcePrivate) != 0 { replyElem := reply.(*message.ReplyElement) id := replyID(replyElem, source) - elem := cqcode.Element{ + elem := msg.Element{ Type: "reply", Data: pairs{ {K: "id", V: strconv.FormatInt(int64(id), 10)}, @@ -132,7 +92,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. r = append(r, elem) } for i, elem := range e { - var m cqcode.Element + var m msg.Element switch o := elem.(type) { case *message.ReplyElement: if base.RemoveReplyAt && i+1 < len(e) { @@ -143,14 +103,14 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. } continue case *message.TextElement: - m = cqcode.Element{ + m = msg.Element{ Type: "text", Data: pairs{ {K: "text", V: o.Content}, }, } case *message.LightAppElement: - m = cqcode.Element{ + m = msg.Element{ Type: "json", Data: pairs{ {K: "data", V: o.Content}, @@ -161,35 +121,35 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if o.Target != 0 { target = strconv.FormatUint(uint64(o.Target), 10) } - m = cqcode.Element{ + m = msg.Element{ Type: "at", Data: pairs{ {K: "qq", V: target}, }, } case *message.RedBagElement: - m = cqcode.Element{ + m = msg.Element{ Type: "redbag", Data: pairs{ {K: "title", V: o.Title}, }, } case *message.ForwardElement: - m = cqcode.Element{ + m = msg.Element{ Type: "forward", Data: pairs{ {K: "id", V: o.ResId}, }, } case *message.FaceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "face", Data: pairs{ {K: "id", V: strconv.FormatInt(int64(o.Index), 10)}, }, } case *message.VoiceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "record", Data: pairs{ {K: "file", V: o.Name}, @@ -197,7 +157,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. }, } case *message.ShortVideoElement: - m = cqcode.Element{ + m = msg.Element{ Type: "video", Data: pairs{ {K: "file", V: o.Name}, @@ -217,7 +177,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. data = append(data, pair{K: "type", V: "show"}) data = append(data, pair{K: "id", V: strconv.FormatInt(int64(o.EffectID), 10)}) } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } @@ -226,7 +186,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. {K: "file", V: hex.EncodeToString(o.Md5) + ".image"}, {K: "url", V: o.Url}, } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } @@ -238,33 +198,33 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if o.Flash { data = append(data, pair{K: "type", V: "flash"}) } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } case *message.DiceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "dice", Data: pairs{ {K: "value", V: strconv.FormatInt(int64(o.Value), 10)}, }, } case *message.FingerGuessingElement: - m = cqcode.Element{ + m = msg.Element{ Type: "rps", Data: pairs{ {K: "value", V: strconv.FormatInt(int64(o.Value), 10)}, }, } case *message.MarketFaceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "text", Data: pairs{ {K: "text", V: o.Name}, }, } case *message.ServiceElement: - m = cqcode.Element{ + m = msg.Element{ Type: "xml", Data: pairs{ {K: "data", V: o.Content}, @@ -275,14 +235,14 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. m.Type = "json" } case *message.AnimatedSticker: - m = cqcode.Element{ + m = msg.Element{ Type: "face", Data: pairs{ {K: "id", V: strconv.FormatInt(int64(o.ID), 10)}, {K: "type", V: "sticker"}, }, } - case *LocalImageElement: + case *msg.LocalImage: data := pairs{ {K: "file", V: o.File}, {K: "url", V: o.URL}, @@ -290,7 +250,7 @@ func toElements(e []message.IMessageElement, source message.Source) (r []cqcode. if o.Flash { data = append(data, pair{K: "type", V: "flash"}) } - m = cqcode.Element{ + m = msg.Element{ Type: "image", Data: data, } @@ -536,11 +496,11 @@ func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType } if i > 0 { if base.SplitURL { - for _, txt := range param.SplitURL(cqcode.UnescapeText(raw[:i])) { + for _, txt := range param.SplitURL(msg.UnescapeText(raw[:i])) { r = append(r, message.NewText(txt)) } } else { - r = append(r, message.NewText(cqcode.UnescapeText(raw[:i]))) + r = append(r, message.NewText(msg.UnescapeText(raw[:i]))) } } @@ -585,7 +545,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType if i+1 > len(raw) { return } - d[key] = cqcode.UnescapeValue(raw[:i]) + d[key] = msg.UnescapeValue(raw[:i]) raw = raw[i:] i = 0 } @@ -755,7 +715,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message } } switch img := e.(type) { - case *LocalImageElement: + case *msg.LocalImage: img.Flash = flash img.EffectID = id case *message.GroupImageElement: @@ -849,7 +809,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return img, err } switch img := img.(type) { - case *LocalImageElement: + case *msg.LocalImage: img.Flash = flash img.EffectID = int32(id) case *message.GroupImageElement: @@ -863,14 +823,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return img, err case "poke": t, _ := strconv.ParseInt(d["qq"], 10, 64) - return &PokeElement{Target: t}, nil + return &msg.Poke{Target: t}, nil case "tts": - defer func() { - if r := recover(); r != nil { - m = nil - err = errors.New("tts 转换失败") - } - }() data, err := bot.Client.GetTts(d["text"]) if err != nil { return nil, err @@ -1024,10 +978,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So return message.NewFingerGuessing(int32(i)), nil case "xml": resID := d["resid"] - template := cqcode.EscapeValue(d["data"]) + template := msg.EscapeValue(d["data"]) i, _ := strconv.ParseInt(resID, 10, 64) - msg := message.NewRichXml(template, i) - return msg, nil + m := message.NewRichXml(template, i) + return m, nil case "json": resID := d["resid"] i, _ := strconv.ParseInt(resID, 10, 64) @@ -1064,7 +1018,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So if err != nil { return nil, err } - v, ok := file.(*LocalVideoElement) + v, ok := file.(*msg.LocalVideo) if !ok { return file, nil } @@ -1081,7 +1035,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So } data, _ = os.ReadFile(v.File + ".jpg") } - v.thumb = bytes.NewReader(data) + v.Thumb = bytes.NewReader(data) video, _ := os.Open(v.File) defer video.Close() _, _ = video.Seek(4, io.SeekStart) @@ -1135,9 +1089,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy } useCacheFile: if video { - return &LocalVideoElement{File: cacheFile}, nil + return &msg.LocalVideo{File: cacheFile}, nil } - return &LocalImageElement{File: cacheFile, URL: f}, nil + return &msg.LocalImage{File: cacheFile, URL: f}, nil } if strings.HasPrefix(f, "file") { fu, err := url.Parse(f) @@ -1158,26 +1112,26 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if info.Size() == 0 || info.Size() >= maxVideoSize { return nil, errors.New("invalid video size") } - return &LocalVideoElement{File: fu.Path}, nil + return &msg.LocalVideo{File: fu.Path}, nil } if info.Size() == 0 || info.Size() >= maxImageSize { return nil, errors.New("invalid image size") } - return &LocalImageElement{File: fu.Path, URL: f}, nil + return &msg.LocalImage{File: fu.Path, URL: f}, nil } if !video && strings.HasPrefix(f, "base64") { b, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(f, "base64://")) if err != nil { return nil, err } - return &LocalImageElement{Stream: bytes.NewReader(b), URL: f}, nil + return &msg.LocalImage{Stream: bytes.NewReader(b), URL: f}, nil } if !video && strings.HasPrefix(f, "base16384") { b, err := b14.UTF82UTF16BE(utils.S2B(strings.TrimPrefix(f, "base16384://"))) if err != nil { return nil, err } - return &LocalImageElement{Stream: bytes.NewReader(b14.Decode(b)), URL: f}, nil + return &msg.LocalImage{Stream: bytes.NewReader(b14.Decode(b)), URL: f}, nil } rawPath := path.Join(global.ImagePath, f) if video { @@ -1194,7 +1148,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return nil, errors.New("invalid video") } if path.Ext(rawPath) != ".video" { - return &LocalVideoElement{File: rawPath}, nil + return &msg.LocalVideo{File: rawPath}, nil } b, _ := os.ReadFile(rawPath) return bot.readVideoCache(b), nil @@ -1203,7 +1157,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy if sourceType == message.SourceGuildChannel { cacheFile := path.Join(global.ImagePath, "guild-images", f) if global.PathExists(cacheFile) { - return &LocalImageElement{File: cacheFile}, nil + return &msg.LocalImage{File: cacheFile}, nil } } if strings.HasSuffix(f, ".image") { @@ -1222,7 +1176,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy return nil, errors.New("invalid image") } if path.Ext(rawPath) != ".image" { - return &LocalImageElement{File: rawPath, URL: u}, nil + return &msg.LocalImage{File: rawPath, URL: u}, nil } b, err := os.ReadFile(rawPath) if err != nil { @@ -1283,7 +1237,7 @@ func (bot *CQBot) makeShowPic(elem message.IMessageElement, source string, brief if brief == "" { brief = "[分享]我看到一张很赞的图片,分享给你,快来看!" } - if local, ok := elem.(*LocalImageElement); ok { + if local, ok := elem.(*msg.LocalImage); ok { r := rand.Uint32() typ := message.SourceGroup if !group { diff --git a/coolq/cqcode/escape.go b/coolq/cqcode/escape.go deleted file mode 100644 index cee9033..0000000 --- a/coolq/cqcode/escape.go +++ /dev/null @@ -1,79 +0,0 @@ -// Package cqcode provides CQCode util functions. -package cqcode - -import "strings" - -// EscapeText 将字符串raw中部分字符转义 -// -// - & -> & -// - [ -> [ -// - ] -> ] -func EscapeText(s string) string { - count := strings.Count(s, "&") - count += strings.Count(s, "[") - count += strings.Count(s, "]") - if count == 0 { - return s - } - - // Apply replacements to buffer. - var b strings.Builder - b.Grow(len(s) + count*4) - start := 0 - for i := 0; i < count; i++ { - j := start - for index, r := range s[start:] { - if r == '&' || r == '[' || r == ']' { - j += index - break - } - } - b.WriteString(s[start:j]) - switch s[j] { - case '&': - b.WriteString("&") - case '[': - b.WriteString("[") - case ']': - b.WriteString("]") - } - start = j + 1 - } - b.WriteString(s[start:]) - return b.String() -} - -// EscapeValue 将字符串value中部分字符转义 -// -// - , -> , -// - & -> & -// - [ -> [ -// - ] -> ] -func EscapeValue(value string) string { - ret := EscapeText(value) - return strings.ReplaceAll(ret, ",", ",") -} - -// UnescapeText 将字符串content中部分字符反转义 -// -// - & -> & -// - [ -> [ -// - ] -> ] -func UnescapeText(content string) string { - ret := content - ret = strings.ReplaceAll(ret, "[", "[") - ret = strings.ReplaceAll(ret, "]", "]") - ret = strings.ReplaceAll(ret, "&", "&") - return ret -} - -// UnescapeValue 将字符串content中部分字符反转义 -// -// - , -> , -// - & -> & -// - [ -> [ -// - ] -> ] -func UnescapeValue(content string) string { - ret := strings.ReplaceAll(content, ",", ",") - return UnescapeText(ret) -} diff --git a/coolq/cqcode_test.go b/coolq/cqcode_test.go index 30c1636..9e5dd89 100644 --- a/coolq/cqcode_test.go +++ b/coolq/cqcode_test.go @@ -10,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/tidwall/gjson" - "github.com/Mrs4s/go-cqhttp/coolq/cqcode" + "github.com/Mrs4s/go-cqhttp/internal/msg" ) var bot = CQBot{} @@ -44,7 +44,7 @@ const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&98765 func BenchmarkCQCodeEscapeText(b *testing.B) { for i := 0; i < b.N; i++ { ret := bText - cqcode.EscapeText(ret) + msg.EscapeText(ret) } } @@ -64,6 +64,6 @@ func TestCQCodeEscapeText(t *testing.T) { ret = strings.ReplaceAll(ret, "&", "&") ret = strings.ReplaceAll(ret, "[", "[") ret = strings.ReplaceAll(ret, "]", "]") - assert.Equal(t, ret, cqcode.EscapeText(rs)) + assert.Equal(t, ret, msg.EscapeText(rs)) } } diff --git a/coolq/cqcode/element.go b/internal/msg/element.go similarity index 63% rename from coolq/cqcode/element.go rename to internal/msg/element.go index 7c1cc74..a787658 100644 --- a/coolq/cqcode/element.go +++ b/internal/msg/element.go @@ -1,4 +1,5 @@ -package cqcode +// Package msg 提供了go-cqhttp消息中间表示,CQ码处理等等 +package msg import ( "bytes" @@ -8,6 +9,85 @@ import ( "github.com/Mrs4s/MiraiGo/binary" ) +// @@@ CQ码转义处理 @@@ + +// EscapeText 将字符串raw中部分字符转义 +// +// - & -> & +// - [ -> [ +// - ] -> ] +func EscapeText(s string) string { + count := strings.Count(s, "&") + count += strings.Count(s, "[") + count += strings.Count(s, "]") + if count == 0 { + return s + } + + // Apply replacements to buffer. + var b strings.Builder + b.Grow(len(s) + count*4) + start := 0 + for i := 0; i < count; i++ { + j := start + for index, r := range s[start:] { + if r == '&' || r == '[' || r == ']' { + j += index + break + } + } + b.WriteString(s[start:j]) + switch s[j] { + case '&': + b.WriteString("&") + case '[': + b.WriteString("[") + case ']': + b.WriteString("]") + } + start = j + 1 + } + b.WriteString(s[start:]) + return b.String() +} + +// EscapeValue 将字符串value中部分字符转义 +// +// - , -> , +// - & -> & +// - [ -> [ +// - ] -> ] +func EscapeValue(value string) string { + ret := EscapeText(value) + return strings.ReplaceAll(ret, ",", ",") +} + +// UnescapeText 将字符串content中部分字符反转义 +// +// - & -> & +// - [ -> [ +// - ] -> ] +func UnescapeText(content string) string { + ret := content + ret = strings.ReplaceAll(ret, "[", "[") + ret = strings.ReplaceAll(ret, "]", "]") + ret = strings.ReplaceAll(ret, "&", "&") + return ret +} + +// UnescapeValue 将字符串content中部分字符反转义 +// +// - , -> , +// - & -> & +// - [ -> [ +// - ] -> ] +func UnescapeValue(content string) string { + ret := strings.ReplaceAll(content, ",", ",") + return UnescapeText(ret) +} + +// @@@ 消息中间表示 @@@ + // Element single message type Element struct { Type string diff --git a/coolq/cqcode/all_test.go b/internal/msg/element_test.go similarity index 96% rename from coolq/cqcode/all_test.go rename to internal/msg/element_test.go index ff285c5..4f37c26 100644 --- a/coolq/cqcode/all_test.go +++ b/internal/msg/element_test.go @@ -1,4 +1,4 @@ -package cqcode +package msg import ( "bytes" diff --git a/internal/msg/local.go b/internal/msg/local.go new file mode 100644 index 0000000..9b8e6e0 --- /dev/null +++ b/internal/msg/local.go @@ -0,0 +1,44 @@ +package msg + +import ( + "io" + + "github.com/Mrs4s/MiraiGo/message" +) + +// Poke 拍一拍 +type Poke struct { + Target int64 +} + +// Type 获取元素类型ID +func (e *Poke) Type() message.ElementType { + // Make message.IMessageElement Happy + return message.At +} + +// LocalImage 本地图片 +type LocalImage struct { + Stream io.ReadSeeker + File string + URL string + + Flash bool + EffectID int32 +} + +// Type implements the message.IMessageElement. +func (e *LocalImage) Type() message.ElementType { + return message.Image +} + +// LocalVideo 本地视频 +type LocalVideo struct { + File string + Thumb io.ReadSeeker +} + +// Type impl message.IMessageElement +func (e *LocalVideo) Type() message.ElementType { + return message.Video +} diff --git a/internal/onebot/spec.go b/internal/onebot/spec.go index 49afe73..bb4431f 100644 --- a/internal/onebot/spec.go +++ b/internal/onebot/spec.go @@ -3,7 +3,7 @@ package onebot import "fmt" -//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go +//go:generate go run ./../../cmd/api-generator -pkg onebot -path=./../../coolq/api.go,./../../coolq/api_v12.go -supported -o supported.go // Spec OneBot Specification type Spec struct { @@ -23,6 +23,7 @@ var V12 = &Spec{ SupportedActions: supportedV12, } +// ConvertID 根据版本转换ID func (s *Spec) ConvertID(id any) any { if s.Version == 12 { return fmt.Sprint(id) diff --git a/modules/api/caller.go b/modules/api/caller.go index 4dfeacf..674074c 100644 --- a/modules/api/caller.go +++ b/modules/api/caller.go @@ -9,7 +9,7 @@ import ( "github.com/Mrs4s/go-cqhttp/internal/onebot" ) -//go:generate go run github.com/Mrs4s/go-cqhttp/cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go +//go:generate go run ./../../cmd/api-generator -pkg api -path=./../../coolq/api.go,./../../coolq/api_v12.go -o api.go // Getter 参数获取 type Getter interface {