mirror of
https://github.com/Mrs4s/go-cqhttp.git
synced 2025-05-04 19:17:37 +08:00
coolq: unified string/array message conversion
change to 2 step: (1): parse to []internal/msg.Element, use msg.ParseObject/msg.ParseString (2): transform to []IMessageElement, can share functions
This commit is contained in:
parent
43dd9aa76d
commit
debc1ed1ae
@ -1847,7 +1847,11 @@ func (bot *CQBot) CQCanSendRecord() global.MSG {
|
|||||||
// @route(ocr_image,".ocr_image")
|
// @route(ocr_image,".ocr_image")
|
||||||
// @rename(image_id->image)
|
// @rename(image_id->image)
|
||||||
func (bot *CQBot) CQOcrImage(imageID string) global.MSG {
|
func (bot *CQBot) CQOcrImage(imageID string) global.MSG {
|
||||||
img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, message.SourceGroup)
|
// TODO: fix this
|
||||||
|
var elem msg.Element
|
||||||
|
elem.Type = "image"
|
||||||
|
elem.Data = []msg.Pair{{K: "file", V: imageID}}
|
||||||
|
img, err := bot.makeImageOrVideoElem(elem, false, message.SourceGroup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("load image error: %v", err)
|
log.Warnf("load image error: %v", err)
|
||||||
return Failed(100, "LOAD_FILE_ERROR", err.Error())
|
return Failed(100, "LOAD_FILE_ERROR", err.Error())
|
||||||
|
640
coolq/cqcode.go
640
coolq/cqcode.go
@ -11,7 +11,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -382,431 +381,163 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) {
|
|||||||
|
|
||||||
// ConvertStringMessage 将消息字符串转为消息元素数组
|
// ConvertStringMessage 将消息字符串转为消息元素数组
|
||||||
func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType) (r []message.IMessageElement) {
|
func (bot *CQBot) ConvertStringMessage(raw string, sourceType message.SourceType) (r []message.IMessageElement) {
|
||||||
var t, key string
|
elems := msg.ParseString(raw)
|
||||||
d := map[string]string{}
|
return bot.ConvertElements(elems, sourceType)
|
||||||
|
|
||||||
saveCQCode := func() {
|
|
||||||
if t == "reply" { // reply 特殊处理
|
|
||||||
if len(r) > 0 {
|
|
||||||
if _, ok := r[0].(*message.ReplyElement); ok {
|
|
||||||
log.Warnf("警告: 一条信息只能包含一个 Reply 元素.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mid, err := strconv.Atoi(d["id"])
|
|
||||||
customText := d["text"]
|
|
||||||
switch {
|
|
||||||
case customText != "":
|
|
||||||
var elem *message.ReplyElement
|
|
||||||
var org db.StoredMessage
|
|
||||||
sender, senderErr := strconv.ParseInt(d["qq"], 10, 64)
|
|
||||||
if senderErr != nil && err != nil {
|
|
||||||
log.Warnf("警告: 自定义 Reply 元素中必须包含 Uin 或 id")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
msgTime, timeErr := strconv.ParseInt(d["time"], 10, 64)
|
|
||||||
if timeErr != nil {
|
|
||||||
msgTime = time.Now().Unix()
|
|
||||||
}
|
|
||||||
messageSeq, seqErr := strconv.ParseInt(d["seq"], 10, 64)
|
|
||||||
if err == nil {
|
|
||||||
org, _ = db.GetMessageByGlobalID(int32(mid))
|
|
||||||
}
|
|
||||||
if org != nil {
|
|
||||||
elem = &message.ReplyElement{
|
|
||||||
ReplySeq: org.GetAttribute().MessageSeq,
|
|
||||||
Sender: org.GetAttribute().SenderUin,
|
|
||||||
Time: int32(org.GetAttribute().Timestamp),
|
|
||||||
Elements: bot.ConvertStringMessage(customText, sourceType),
|
|
||||||
}
|
|
||||||
if senderErr != nil {
|
|
||||||
elem.Sender = sender
|
|
||||||
}
|
|
||||||
if timeErr != nil {
|
|
||||||
elem.Time = int32(msgTime)
|
|
||||||
}
|
|
||||||
if seqErr != nil {
|
|
||||||
elem.ReplySeq = int32(messageSeq)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
elem = &message.ReplyElement{
|
|
||||||
ReplySeq: int32(messageSeq),
|
|
||||||
Sender: sender,
|
|
||||||
Time: int32(msgTime),
|
|
||||||
Elements: bot.ConvertStringMessage(customText, sourceType),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r = append([]message.IMessageElement{elem}, r...)
|
|
||||||
case err == nil:
|
|
||||||
org, err := db.GetMessageByGlobalID(int32(mid))
|
|
||||||
if err == nil {
|
|
||||||
r = append([]message.IMessageElement{
|
|
||||||
&message.ReplyElement{
|
|
||||||
ReplySeq: org.GetAttribute().MessageSeq,
|
|
||||||
Sender: org.GetAttribute().SenderUin,
|
|
||||||
Time: int32(org.GetAttribute().Timestamp),
|
|
||||||
Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
|
|
||||||
},
|
|
||||||
}, r...)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Warnf("警告: Reply 元素中必须包含 text 或 id")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if t == "forward" { // 单独处理转发
|
|
||||||
if id, ok := d["id"]; ok {
|
|
||||||
if fwdMsg := bot.Client.DownloadForwardMessage(id); fwdMsg == nil {
|
|
||||||
log.Warnf("警告: Forward 信息不存在或已过期")
|
|
||||||
} else {
|
|
||||||
r = []message.IMessageElement{fwdMsg}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warnf("警告: Forward 元素中必须包含 id")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
elem, err := bot.ToElement(t, d, sourceType)
|
|
||||||
if err != nil {
|
|
||||||
org := "[CQ:" + t
|
|
||||||
for k, v := range d {
|
|
||||||
org += "," + k + "=" + v
|
|
||||||
}
|
|
||||||
org += "]"
|
|
||||||
if !base.IgnoreInvalidCQCode {
|
|
||||||
log.Warnf("转换CQ码 %v 时出现错误: %v 将原样发送.", org, err)
|
|
||||||
r = append(r, message.NewText(org))
|
|
||||||
} else {
|
|
||||||
log.Warnf("转换CQ码 %v 时出现错误: %v 将忽略.", org, err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch i := elem.(type) {
|
|
||||||
case message.IMessageElement:
|
|
||||||
r = append(r, i)
|
|
||||||
case []message.IMessageElement:
|
|
||||||
r = append(r, i...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for raw != "" {
|
|
||||||
i := 0
|
|
||||||
for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i > 0 {
|
|
||||||
if base.SplitURL {
|
|
||||||
for _, txt := range param.SplitURL(msg.UnescapeText(raw[:i])) {
|
|
||||||
r = append(r, message.NewText(txt))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
r = append(r, message.NewText(msg.UnescapeText(raw[:i])))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if i+4 > len(raw) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
raw = raw[i+4:] // skip "[CQ:"
|
|
||||||
i = 0
|
|
||||||
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i+1 > len(raw) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t = raw[:i]
|
|
||||||
for k := range d { // clear the map, reuse it
|
|
||||||
delete(d, k)
|
|
||||||
}
|
|
||||||
raw = raw[i:]
|
|
||||||
i = 0
|
|
||||||
for {
|
|
||||||
if raw[0] == ']' {
|
|
||||||
saveCQCode()
|
|
||||||
raw = raw[1:]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
raw = raw[1:]
|
|
||||||
|
|
||||||
for i < len(raw) && raw[i] != '=' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
if i+1 > len(raw) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
key = raw[:i]
|
|
||||||
raw = raw[i+1:] // skip "="
|
|
||||||
i = 0
|
|
||||||
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
if i+1 > len(raw) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
d[key] = msg.UnescapeValue(raw[:i])
|
|
||||||
raw = raw[i:]
|
|
||||||
i = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertObjectMessage 将消息JSON对象转为消息元素数组
|
// ConvertObjectMessage 将消息JSON对象转为消息元素数组
|
||||||
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) {
|
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType message.SourceType) (r []message.IMessageElement) {
|
||||||
d := make(map[string]string)
|
if m.Type == gjson.String {
|
||||||
convertElem := func(e gjson.Result) {
|
return bot.ConvertStringMessage(m.Str, sourceType)
|
||||||
t := e.Get("type").Str
|
}
|
||||||
if t == "reply" && sourceType&(message.SourceGroup|message.SourcePrivate) != 0 {
|
elems := msg.ParseObject(m)
|
||||||
if len(r) > 0 {
|
return bot.ConvertElements(elems, sourceType)
|
||||||
if _, ok := r[0].(*message.ReplyElement); ok {
|
}
|
||||||
log.Warnf("警告: 一条信息只能包含一个 Reply 元素.")
|
|
||||||
return
|
// ConvertContentMessage 将数据库用的 content 转换为消息元素数组
|
||||||
}
|
func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType) (r []message.IMessageElement) {
|
||||||
}
|
elems := make([]msg.Element, len(content))
|
||||||
mid, err := strconv.Atoi(e.Get("data.id").String())
|
for i, v := range content {
|
||||||
customText := e.Get("data.text").String()
|
elem := msg.Element{Type: v["type"].(string)}
|
||||||
switch {
|
for k, v := range v["data"].(global.MSG) {
|
||||||
case customText != "":
|
pair := msg.Pair{K: k, V: fmt.Sprint(v)}
|
||||||
var elem *message.ReplyElement
|
elem.Data = append(elem.Data, pair)
|
||||||
var org db.StoredMessage
|
|
||||||
sender, senderErr := strconv.ParseInt(e.Get("data.[user_id,qq]").String(), 10, 64)
|
|
||||||
if senderErr != nil && err != nil {
|
|
||||||
log.Warnf("警告: 自定义 Reply 元素中必须包含 user_id 或 id")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
msgTime, timeErr := strconv.ParseInt(e.Get("data.time").String(), 10, 64)
|
|
||||||
if timeErr != nil {
|
|
||||||
msgTime = time.Now().Unix()
|
|
||||||
}
|
|
||||||
messageSeq, seqErr := strconv.ParseInt(e.Get("data.seq").String(), 10, 64)
|
|
||||||
if err == nil {
|
|
||||||
org, _ = db.GetMessageByGlobalID(int32(mid))
|
|
||||||
}
|
|
||||||
if org != nil {
|
|
||||||
elem = &message.ReplyElement{
|
|
||||||
ReplySeq: org.GetAttribute().MessageSeq,
|
|
||||||
Sender: org.GetAttribute().SenderUin,
|
|
||||||
Time: int32(org.GetAttribute().Timestamp),
|
|
||||||
Elements: bot.ConvertStringMessage(customText, sourceType),
|
|
||||||
}
|
|
||||||
if senderErr != nil {
|
|
||||||
elem.Sender = sender
|
|
||||||
}
|
|
||||||
if timeErr != nil {
|
|
||||||
elem.Time = int32(msgTime)
|
|
||||||
}
|
|
||||||
if seqErr != nil {
|
|
||||||
elem.ReplySeq = int32(messageSeq)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
elem = &message.ReplyElement{
|
|
||||||
ReplySeq: int32(messageSeq),
|
|
||||||
Sender: sender,
|
|
||||||
Time: int32(msgTime),
|
|
||||||
Elements: bot.ConvertStringMessage(customText, sourceType),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
r = append([]message.IMessageElement{elem}, r...)
|
|
||||||
case err == nil:
|
|
||||||
org, err := db.GetMessageByGlobalID(int32(mid))
|
|
||||||
if err == nil {
|
|
||||||
r = append([]message.IMessageElement{
|
|
||||||
&message.ReplyElement{
|
|
||||||
ReplySeq: org.GetAttribute().MessageSeq,
|
|
||||||
Sender: org.GetAttribute().SenderUin,
|
|
||||||
Time: int32(org.GetAttribute().Timestamp),
|
|
||||||
Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
|
|
||||||
},
|
|
||||||
}, r...)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
log.Warnf("警告: Reply 元素中必须包含 text 或 id")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if t == "forward" {
|
elems[i] = elem
|
||||||
id := e.Get("data.id").String()
|
}
|
||||||
if id == "" {
|
return bot.ConvertElements(elems, sourceType)
|
||||||
log.Warnf("警告: Forward 元素中必须包含 id")
|
}
|
||||||
} else {
|
|
||||||
if fwdMsg := bot.Client.DownloadForwardMessage(id); fwdMsg == nil {
|
// ConvertElements 将解码后的消息数组转换为MiraiGo表示
|
||||||
log.Warnf("警告: Forward 信息不存在或已过期")
|
func (bot *CQBot) ConvertElements(elems []msg.Element, sourceType message.SourceType) (r []message.IMessageElement) {
|
||||||
} else {
|
var replyCount int
|
||||||
r = []message.IMessageElement{fwdMsg}
|
for _, elem := range elems {
|
||||||
}
|
me, err := bot.ConvertElement(elem, sourceType)
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for i := range d {
|
|
||||||
delete(d, i)
|
|
||||||
}
|
|
||||||
e.Get("data").ForEach(func(key, value gjson.Result) bool {
|
|
||||||
d[key.Str] = value.String()
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
elem, err := bot.ToElement(t, d, sourceType)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warnf("转换CQ码 (%v) 到MiraiGo Element时出现错误: %v 将忽略本段CQ码.", e.Raw, err)
|
// TODO: don't use cqcode format
|
||||||
return
|
if !base.IgnoreInvalidCQCode {
|
||||||
|
r = append(r, message.NewText(elem.CQCode()))
|
||||||
|
}
|
||||||
|
log.Warnf("转换消息 %v 到MiraiGo Element时出现错误: %v.", elem.CQCode(), err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
switch i := elem.(type) {
|
switch i := me.(type) {
|
||||||
|
case *message.ReplyElement:
|
||||||
|
if replyCount > 0 {
|
||||||
|
log.Warnf("警告: 一条信息只能包含一个 Reply 元素.")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
replyCount++
|
||||||
|
// 将回复消息放置于第一个
|
||||||
|
r = append([]message.IMessageElement{i}, r...)
|
||||||
case message.IMessageElement:
|
case message.IMessageElement:
|
||||||
r = append(r, i)
|
r = append(r, i)
|
||||||
case []message.IMessageElement:
|
case []message.IMessageElement:
|
||||||
r = append(r, i...)
|
r = append(r, i...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if m.Type == gjson.String {
|
|
||||||
return bot.ConvertStringMessage(m.Str, sourceType)
|
|
||||||
}
|
|
||||||
if m.IsArray() {
|
|
||||||
m.ForEach(func(_, e gjson.Result) bool {
|
|
||||||
convertElem(e)
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if m.IsObject() {
|
|
||||||
convertElem(m)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertContentMessage 将数据库用的 content 转换为消息元素数组
|
func (bot *CQBot) reply(elem msg.Element, sourceType message.SourceType) (any, error) {
|
||||||
func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType message.SourceType) (r []message.IMessageElement) {
|
mid, err := strconv.Atoi(elem.Get("id"))
|
||||||
for _, c := range content {
|
customText := elem.Get("text")
|
||||||
data := c["data"].(global.MSG)
|
var re *message.ReplyElement
|
||||||
switch c["type"] {
|
switch {
|
||||||
case "text":
|
case customText != "":
|
||||||
r = append(r, message.NewText(data["text"].(string)))
|
var org db.StoredMessage
|
||||||
case "image":
|
sender, senderErr := strconv.ParseInt(elem.Get("user_id"), 10, 64)
|
||||||
u, ok := data["url"]
|
if senderErr != nil {
|
||||||
d := make(map[string]string, 2)
|
sender, senderErr = strconv.ParseInt(elem.Get("qq"), 10, 64)
|
||||||
if ok {
|
|
||||||
d["url"] = u.(string)
|
|
||||||
}
|
|
||||||
d["file"] = data["file"].(string)
|
|
||||||
e, err := bot.makeImageOrVideoElem(d, false, sourceType)
|
|
||||||
if err != nil {
|
|
||||||
log.Warnf("make image elem error: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
flash, id := false, int32(0)
|
|
||||||
if t, ok := data["type"]; ok {
|
|
||||||
if t.(string) == "flash" {
|
|
||||||
flash = true
|
|
||||||
}
|
|
||||||
if t.(string) == "show" {
|
|
||||||
id := 0
|
|
||||||
switch idn := data["id"].(type) {
|
|
||||||
case int32:
|
|
||||||
id = int(idn)
|
|
||||||
case int:
|
|
||||||
id = idn
|
|
||||||
case int64:
|
|
||||||
id = int(idn)
|
|
||||||
default:
|
|
||||||
id = int(reflect.ValueOf(data["id"]).Convert(reflect.TypeOf(0)).Int())
|
|
||||||
}
|
|
||||||
if id < 40000 || id >= 40006 {
|
|
||||||
id = 40000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch img := e.(type) {
|
|
||||||
case *msg.LocalImage:
|
|
||||||
img.Flash = flash
|
|
||||||
img.EffectID = id
|
|
||||||
case *message.GroupImageElement:
|
|
||||||
img.Flash = flash
|
|
||||||
img.EffectID = id
|
|
||||||
switch sub := data["subType"].(type) {
|
|
||||||
case int64:
|
|
||||||
img.ImageBizType = message.ImageBizType(sub)
|
|
||||||
case uint32:
|
|
||||||
img.ImageBizType = message.ImageBizType(sub)
|
|
||||||
}
|
|
||||||
case *message.FriendImageElement:
|
|
||||||
img.Flash = flash
|
|
||||||
}
|
|
||||||
r = append(r, e)
|
|
||||||
case "at":
|
|
||||||
switch data["subType"].(string) {
|
|
||||||
case "all":
|
|
||||||
r = append(r, message.NewAt(0))
|
|
||||||
case "user":
|
|
||||||
r = append(r, message.NewAt(reflect.ValueOf(data["target"]).Int(), data["display"].(string)))
|
|
||||||
default:
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case "redbag":
|
|
||||||
r = append(r, &message.RedBagElement{
|
|
||||||
MsgType: message.RedBagMessageType(data["type"].(int)),
|
|
||||||
Title: data["title"].(string),
|
|
||||||
})
|
|
||||||
case "forward":
|
|
||||||
r = append(r, &message.ForwardElement{
|
|
||||||
ResId: data["id"].(string),
|
|
||||||
})
|
|
||||||
case "face":
|
|
||||||
id := int32(0)
|
|
||||||
switch idn := data["id"].(type) {
|
|
||||||
case int32:
|
|
||||||
id = idn
|
|
||||||
case int:
|
|
||||||
id = int32(idn)
|
|
||||||
case int64:
|
|
||||||
id = int32(idn)
|
|
||||||
default:
|
|
||||||
id = int32(reflect.ValueOf(data["id"]).Convert(reflect.TypeOf(0)).Int())
|
|
||||||
}
|
|
||||||
r = append(r, message.NewFace(id))
|
|
||||||
case "video":
|
|
||||||
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
|
|
||||||
}
|
|
||||||
r = append(r, e)
|
|
||||||
}
|
}
|
||||||
|
if senderErr != nil && err != nil {
|
||||||
|
return nil, errors.New("警告: 自定义 reply 元素中必须包含 user_id 或 id")
|
||||||
|
}
|
||||||
|
msgTime, timeErr := strconv.ParseInt(elem.Get("time"), 10, 64)
|
||||||
|
if timeErr != nil {
|
||||||
|
msgTime = time.Now().Unix()
|
||||||
|
}
|
||||||
|
messageSeq, seqErr := strconv.ParseInt(elem.Get("seq"), 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
org, _ = db.GetMessageByGlobalID(int32(mid))
|
||||||
|
}
|
||||||
|
if org != nil {
|
||||||
|
re = &message.ReplyElement{
|
||||||
|
ReplySeq: org.GetAttribute().MessageSeq,
|
||||||
|
Sender: org.GetAttribute().SenderUin,
|
||||||
|
Time: int32(org.GetAttribute().Timestamp),
|
||||||
|
Elements: bot.ConvertStringMessage(customText, sourceType),
|
||||||
|
}
|
||||||
|
if senderErr != nil {
|
||||||
|
re.Sender = sender
|
||||||
|
}
|
||||||
|
if timeErr != nil {
|
||||||
|
re.Time = int32(msgTime)
|
||||||
|
}
|
||||||
|
if seqErr != nil {
|
||||||
|
re.ReplySeq = int32(messageSeq)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
re = &message.ReplyElement{
|
||||||
|
ReplySeq: int32(messageSeq),
|
||||||
|
Sender: sender,
|
||||||
|
Time: int32(msgTime),
|
||||||
|
Elements: bot.ConvertStringMessage(customText, sourceType),
|
||||||
|
}
|
||||||
|
|
||||||
|
case err == nil:
|
||||||
|
org, err := db.GetMessageByGlobalID(int32(mid))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
re = &message.ReplyElement{
|
||||||
|
ReplySeq: org.GetAttribute().MessageSeq,
|
||||||
|
Sender: org.GetAttribute().SenderUin,
|
||||||
|
Time: int32(org.GetAttribute().Timestamp),
|
||||||
|
Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, errors.New("reply消息中必须包含 text 或 id")
|
||||||
}
|
}
|
||||||
return
|
return re, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToElement 将解码后的CQCode转换为Element.
|
// ConvertElement 将解码后的消息转换为MiraiGoElement.
|
||||||
//
|
//
|
||||||
// 返回 interface{} 存在三种类型
|
// 返回 interface{} 存在三种类型
|
||||||
//
|
//
|
||||||
// message.IMessageElement []message.IMessageElement nil
|
// message.IMessageElement []message.IMessageElement nil
|
||||||
func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.SourceType) (m any, err error) {
|
func (bot *CQBot) ConvertElement(elem msg.Element, sourceType message.SourceType) (m any, err error) {
|
||||||
switch t {
|
switch elem.Type {
|
||||||
case "text":
|
case "text":
|
||||||
|
text := elem.Get("text")
|
||||||
if base.SplitURL {
|
if base.SplitURL {
|
||||||
var ret []message.IMessageElement
|
var ret []message.IMessageElement
|
||||||
for _, text := range param.SplitURL(d["text"]) {
|
for _, text := range param.SplitURL(text) {
|
||||||
ret = append(ret, message.NewText(text))
|
ret = append(ret, message.NewText(text))
|
||||||
}
|
}
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
return message.NewText(d["text"]), nil
|
return message.NewText(text), nil
|
||||||
case "image":
|
case "image":
|
||||||
img, err := bot.makeImageOrVideoElem(d, false, sourceType)
|
img, err := bot.makeImageOrVideoElem(elem, false, sourceType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tp := d["type"]
|
tp := elem.Get("type")
|
||||||
flash, id := false, int64(0)
|
flash, id := false, int64(0)
|
||||||
switch tp {
|
switch tp {
|
||||||
case "flash":
|
case "flash":
|
||||||
flash = true
|
flash = true
|
||||||
case "show":
|
case "show":
|
||||||
id, _ = strconv.ParseInt(d["id"], 10, 64)
|
id, _ = strconv.ParseInt(elem.Get("id"), 10, 64)
|
||||||
if id < 40000 || id >= 40006 {
|
if id < 40000 || id >= 40006 {
|
||||||
id = 40000
|
id = 40000
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return img, err
|
return img, nil
|
||||||
}
|
}
|
||||||
switch img := img.(type) {
|
switch img := img.(type) {
|
||||||
case *msg.LocalImage:
|
case *msg.LocalImage:
|
||||||
@ -815,24 +546,37 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
case *message.GroupImageElement:
|
case *message.GroupImageElement:
|
||||||
img.Flash = flash
|
img.Flash = flash
|
||||||
img.EffectID = int32(id)
|
img.EffectID = int32(id)
|
||||||
i, _ := strconv.ParseInt(d["subType"], 10, 64)
|
i, _ := strconv.ParseInt(elem.Get("subType"), 10, 64)
|
||||||
img.ImageBizType = message.ImageBizType(i)
|
img.ImageBizType = message.ImageBizType(i)
|
||||||
case *message.FriendImageElement:
|
case *message.FriendImageElement:
|
||||||
img.Flash = flash
|
img.Flash = flash
|
||||||
}
|
}
|
||||||
return img, err
|
return img, nil
|
||||||
|
case "reply":
|
||||||
|
return bot.reply(elem, sourceType)
|
||||||
|
case "forward":
|
||||||
|
id := elem.Get("id")
|
||||||
|
if id != "" {
|
||||||
|
return nil, errors.New("forward 消息中必须包含 id")
|
||||||
|
}
|
||||||
|
fwdMsg := bot.Client.DownloadForwardMessage(id)
|
||||||
|
if fwdMsg == nil {
|
||||||
|
return nil, errors.New("forward 消息不存在或已过期")
|
||||||
|
}
|
||||||
|
return fwdMsg, nil
|
||||||
|
|
||||||
case "poke":
|
case "poke":
|
||||||
t, _ := strconv.ParseInt(d["qq"], 10, 64)
|
t, _ := strconv.ParseInt(elem.Get("qq"), 10, 64)
|
||||||
return &msg.Poke{Target: t}, nil
|
return &msg.Poke{Target: t}, nil
|
||||||
case "tts":
|
case "tts":
|
||||||
data, err := bot.Client.GetTts(d["text"])
|
data, err := bot.Client.GetTts(elem.Get("text"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &message.VoiceElement{Data: base.ResampleSilk(data)}, nil
|
return &message.VoiceElement{Data: base.ResampleSilk(data)}, nil
|
||||||
case "record", "audio":
|
case "record", "audio":
|
||||||
f := d["file"]
|
f := elem.Get("file")
|
||||||
data, err := global.FindFile(f, d["cache"], global.VoicePath)
|
data, err := global.FindFile(f, elem.Get("cache"), global.VoicePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -848,19 +592,18 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
}
|
}
|
||||||
return &message.VoiceElement{Data: data}, nil
|
return &message.VoiceElement{Data: data}, nil
|
||||||
case "face":
|
case "face":
|
||||||
id, err := strconv.Atoi(d["id"])
|
id, err := strconv.Atoi(elem.Get("id"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if d["type"] == "sticker" {
|
if elem.Get("type") == "sticker" {
|
||||||
return &message.AnimatedSticker{ID: int32(id)}, nil
|
return &message.AnimatedSticker{ID: int32(id)}, nil
|
||||||
}
|
}
|
||||||
return message.NewFace(int32(id)), nil
|
return message.NewFace(int32(id)), nil
|
||||||
case "mention_all":
|
case "mention_all":
|
||||||
d["qq"] = "all"
|
return message.AtAll(), nil
|
||||||
fallthrough
|
|
||||||
case "at", "mention":
|
case "at", "mention":
|
||||||
qq := d["qq"]
|
qq := elem.Get("qq")
|
||||||
if qq == "all" {
|
if qq == "all" {
|
||||||
return message.AtAll(), nil
|
return message.AtAll(), nil
|
||||||
}
|
}
|
||||||
@ -868,16 +611,17 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
name := strings.TrimSpace(d["name"])
|
name := strings.TrimSpace(elem.Get("name"))
|
||||||
if len(name) > 0 {
|
if len(name) > 0 {
|
||||||
name = "@" + name
|
name = "@" + name
|
||||||
}
|
}
|
||||||
return message.NewAt(t, name), nil
|
return message.NewAt(t, name), nil
|
||||||
case "share":
|
case "share":
|
||||||
return message.NewUrlShare(d["url"], d["title"], d["content"], d["image"]), nil
|
return message.NewUrlShare(elem.Get("url"), elem.Get("title"), elem.Get("content"), elem.Get("image")), nil
|
||||||
case "music":
|
case "music":
|
||||||
if d["type"] == "qq" {
|
id := elem.Get("id")
|
||||||
info, err := global.QQMusicSongInfo(d["id"])
|
if elem.Get("type") == "qq" {
|
||||||
|
info, err := global.QQMusicSongInfo(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -887,13 +631,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
name := info.Get("track_info.name").Str
|
name := info.Get("track_info.name").Str
|
||||||
mid := info.Get("track_info.mid").Str
|
mid := info.Get("track_info.mid").Str
|
||||||
albumMid := info.Get("track_info.album.mid").Str
|
albumMid := info.Get("track_info.album.mid").Str
|
||||||
pinfo, _ := download.Request{URL: "http://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON()
|
pinfo, _ := download.Request{URL: "https://u.y.qq.com/cgi-bin/musicu.fcg?g_tk=2034008533&uin=0&format=json&data={\"comm\":{\"ct\":23,\"cv\":0},\"url_mid\":{\"module\":\"vkey.GetVkeyServer\",\"method\":\"CgiGetVkey\",\"param\":{\"guid\":\"4311206557\",\"songmid\":[\"" + mid + "\"],\"songtype\":[0],\"uin\":\"0\",\"loginflag\":1,\"platform\":\"23\"}}}&_=1599039471576"}.JSON()
|
||||||
jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare"
|
jumpURL := "https://i.y.qq.com/v8/playsong.html?platform=11&appshare=android_qq&appversion=10030010&hosteuin=oKnlNenz7i-s7c**&songmid=" + mid + "&type=0&appsongtype=1&_wv=1&source=qq&ADTAG=qfshare"
|
||||||
purl := pinfo.Get("url_mid.data.midurlinfo.0.purl").Str
|
purl := pinfo.Get("url_mid.data.midurlinfo.0.purl").Str
|
||||||
preview := "http://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg"
|
preview := "https://y.gtimg.cn/music/photo_new/T002R180x180M000" + albumMid + ".jpg"
|
||||||
content := info.Get("track_info.singer.0.name").Str
|
content := info.Get("track_info.singer.0.name").Str
|
||||||
if d["content"] != "" {
|
if elem.Get("content") != "" {
|
||||||
content = d["content"]
|
content = elem.Get("content")
|
||||||
}
|
}
|
||||||
return &message.MusicShareElement{
|
return &message.MusicShareElement{
|
||||||
MusicType: message.QQMusic,
|
MusicType: message.QQMusic,
|
||||||
@ -904,8 +648,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
MusicUrl: purl,
|
MusicUrl: purl,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if d["type"] == "163" {
|
if elem.Get("type") == "163" {
|
||||||
info, err := global.NeteaseMusicSongInfo(d["id"])
|
info, err := global.NeteaseMusicSongInfo(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -913,8 +657,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
return nil, errors.New("song not found")
|
return nil, errors.New("song not found")
|
||||||
}
|
}
|
||||||
name := info.Get("name").Str
|
name := info.Get("name").Str
|
||||||
jumpURL := "https://y.music.163.com/m/song/" + d["id"]
|
jumpURL := "https://y.music.163.com/m/song/" + id
|
||||||
musicURL := "http://music.163.com/song/media/outer/url?id=" + d["id"]
|
musicURL := "http://music.163.com/song/media/outer/url?id=" + id
|
||||||
picURL := info.Get("album.picUrl").Str
|
picURL := info.Get("album.picUrl").Str
|
||||||
artistName := ""
|
artistName := ""
|
||||||
if info.Get("artists.0").Exists() {
|
if info.Get("artists.0").Exists() {
|
||||||
@ -929,10 +673,10 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
MusicUrl: musicURL,
|
MusicUrl: musicURL,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
if d["type"] == "custom" {
|
if elem.Get("type") == "custom" {
|
||||||
if d["subtype"] != "" {
|
if elem.Get("subtype") != "" {
|
||||||
var subType int
|
var subType int
|
||||||
switch d["subtype"] {
|
switch elem.Get("subtype") {
|
||||||
default:
|
default:
|
||||||
subType = message.QQMusic
|
subType = message.QQMusic
|
||||||
case "163":
|
case "163":
|
||||||
@ -946,59 +690,58 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
}
|
}
|
||||||
return &message.MusicShareElement{
|
return &message.MusicShareElement{
|
||||||
MusicType: subType,
|
MusicType: subType,
|
||||||
Title: d["title"],
|
Title: elem.Get("title"),
|
||||||
Summary: d["content"],
|
Summary: elem.Get("content"),
|
||||||
Url: d["url"],
|
Url: elem.Get("url"),
|
||||||
PictureUrl: d["image"],
|
PictureUrl: elem.Get("image"),
|
||||||
MusicUrl: d["audio"],
|
MusicUrl: elem.Get("audio"),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
xml := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="2" templateID="1" action="web" brief="[分享] %s" sourceMsgId="0" url="%s" flag="0" adverSign="0" multiMsgFlag="0"><item layout="2"><audio cover="%s" src="%s"/><title>%s</title><summary>%s</summary></item><source name="音乐" icon="https://i.gtimg.cn/open/app_icon/01/07/98/56/1101079856_100_m.png" url="http://web.p.qq.com/qqmpmobile/aio/app.html?id=1101079856" action="app" a_actionData="com.tencent.qqmusic" i_actionData="tencent1101079856://" appid="1101079856" /></msg>`,
|
xml := fmt.Sprintf(`<?xml version='1.0' encoding='UTF-8' standalone='yes' ?><msg serviceID="2" templateID="1" action="web" brief="[分享] %s" sourceMsgId="0" url="%s" flag="0" adverSign="0" multiMsgFlag="0"><item layout="2"><audio cover="%s" src="%s"/><title>%s</title><summary>%s</summary></item><source name="音乐" icon="https://i.gtimg.cn/open/app_icon/01/07/98/56/1101079856_100_m.png" url="http://web.p.qq.com/qqmpmobile/aio/app.html?id=1101079856" action="app" a_actionData="com.tencent.qqmusic" i_actionData="tencent1101079856://" appid="1101079856" /></msg>`,
|
||||||
utils.XmlEscape(d["title"]), d["url"], d["image"], d["audio"], utils.XmlEscape(d["title"]), utils.XmlEscape(d["content"]))
|
utils.XmlEscape(elem.Get("title")), elem.Get("url"), elem.Get("image"), elem.Get("audio"), utils.XmlEscape(elem.Get("title")), utils.XmlEscape(elem.Get("content")))
|
||||||
return &message.ServiceElement{
|
return &message.ServiceElement{
|
||||||
Id: 60,
|
Id: 60,
|
||||||
Content: xml,
|
Content: xml,
|
||||||
SubType: "music",
|
SubType: "music",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, errors.New("unsupported music type: " + d["type"])
|
return nil, errors.New("unsupported music type: " + elem.Get("type"))
|
||||||
case "dice":
|
case "dice":
|
||||||
value := d["value"]
|
value := elem.Get("value")
|
||||||
i, _ := strconv.ParseInt(value, 10, 64)
|
i, _ := strconv.ParseInt(value, 10, 64)
|
||||||
if i < 0 || i > 6 {
|
if i < 0 || i > 6 {
|
||||||
return nil, errors.New("invalid dice value " + value)
|
return nil, errors.New("invalid dice value " + value)
|
||||||
}
|
}
|
||||||
return message.NewDice(int32(i)), nil
|
return message.NewDice(int32(i)), nil
|
||||||
case "rps":
|
case "rps":
|
||||||
value := d["value"]
|
value := elem.Get("value")
|
||||||
i, _ := strconv.ParseInt(value, 10, 64)
|
i, _ := strconv.ParseInt(value, 10, 64)
|
||||||
if i < 0 || i > 2 {
|
if i < 0 || i > 2 {
|
||||||
return nil, errors.New("invalid finger-guessing value " + value)
|
return nil, errors.New("invalid finger-guessing value " + value)
|
||||||
}
|
}
|
||||||
return message.NewFingerGuessing(int32(i)), nil
|
return message.NewFingerGuessing(int32(i)), nil
|
||||||
case "xml":
|
case "xml":
|
||||||
resID := d["resid"]
|
resID := elem.Get("resid")
|
||||||
template := msg.EscapeValue(d["data"])
|
template := elem.Get("data")
|
||||||
i, _ := strconv.ParseInt(resID, 10, 64)
|
i, _ := strconv.ParseInt(resID, 10, 64)
|
||||||
m := message.NewRichXml(template, i)
|
m := message.NewRichXml(template, i)
|
||||||
return m, nil
|
return m, nil
|
||||||
case "json":
|
case "json":
|
||||||
resID := d["resid"]
|
resID := elem.Get("resid")
|
||||||
|
data := elem.Get("data")
|
||||||
i, _ := strconv.ParseInt(resID, 10, 64)
|
i, _ := strconv.ParseInt(resID, 10, 64)
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
// 默认情况下走小程序通道
|
// 默认情况下走小程序通道
|
||||||
msg := message.NewLightApp(d["data"])
|
return message.NewLightApp(data), nil
|
||||||
return msg, nil
|
|
||||||
}
|
}
|
||||||
// resid不为0的情况下走富文本通道,后续补全透传service Id,此处暂时不处理 TODO
|
// resid不为0的情况下走富文本通道,后续补全透传service Id,此处暂时不处理 TODO
|
||||||
msg := message.NewRichJson(d["data"])
|
return message.NewRichJson(data), nil
|
||||||
return msg, nil
|
|
||||||
case "cardimage":
|
case "cardimage":
|
||||||
source := d["source"]
|
source := elem.Get("source")
|
||||||
icon := d["icon"]
|
icon := elem.Get("icon")
|
||||||
brief := d["brief"]
|
brief := elem.Get("brief")
|
||||||
parseIntWithDefault := func(name string, origin int64) int64 {
|
parseIntWithDefault := func(name string, origin int64) int64 {
|
||||||
v, _ := strconv.ParseInt(d[name], 10, 64)
|
v, _ := strconv.ParseInt(elem.Get(name), 10, 64)
|
||||||
if v <= 0 {
|
if v <= 0 {
|
||||||
return origin
|
return origin
|
||||||
}
|
}
|
||||||
@ -1008,13 +751,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
maxWidth := parseIntWithDefault("maxwidth", 500)
|
maxWidth := parseIntWithDefault("maxwidth", 500)
|
||||||
minHeight := parseIntWithDefault("minheight", 200)
|
minHeight := parseIntWithDefault("minheight", 200)
|
||||||
maxHeight := parseIntWithDefault("maxheight", 1000)
|
maxHeight := parseIntWithDefault("maxheight", 1000)
|
||||||
img, err := bot.makeImageOrVideoElem(d, false, sourceType)
|
img, err := bot.makeImageOrVideoElem(elem, false, sourceType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("send cardimage faild")
|
return nil, errors.New("send cardimage faild")
|
||||||
}
|
}
|
||||||
return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == message.SourceGroup)
|
return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == message.SourceGroup)
|
||||||
case "video":
|
case "video":
|
||||||
file, err := bot.makeImageOrVideoElem(d, true, sourceType)
|
file, err := bot.makeImageOrVideoElem(elem, true, sourceType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -1026,8 +769,8 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
var data []byte
|
var data []byte
|
||||||
if cover, ok := d["cover"]; ok {
|
if cover := elem.Get("cover"); cover != "" {
|
||||||
data, _ = global.FindFile(cover, d["cache"], global.ImagePath)
|
data, _ = global.FindFile(cover, elem.Get("cache"), global.ImagePath)
|
||||||
} else {
|
} else {
|
||||||
err = global.ExtractCover(v.File, v.File+".jpg")
|
err = global.ExtractCover(v.File, v.File+".jpg")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1045,7 +788,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
_, _ = video.Seek(0, io.SeekStart)
|
_, _ = video.Seek(0, io.SeekStart)
|
||||||
hash, _ := utils.ComputeMd5AndLength(video)
|
hash, _ := utils.ComputeMd5AndLength(video)
|
||||||
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".mp4")
|
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash)+".mp4")
|
||||||
if !(d["cache"] == "" || d["cache"] == "1") || !global.PathExists(cacheFile) {
|
if !(elem.Get("cache") == "" || elem.Get("cache") == "1") || !global.PathExists(cacheFile) {
|
||||||
err = global.EncodeMP4(v.File, cacheFile)
|
err = global.EncodeMP4(v.File, cacheFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -1055,17 +798,14 @@ func (bot *CQBot) ToElement(t string, d map[string]string, sourceType message.So
|
|||||||
}
|
}
|
||||||
return v, nil
|
return v, nil
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("unsupported cq code: " + t)
|
return nil, errors.New("unsupported message type: " + elem.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用
|
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用
|
||||||
func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType message.SourceType) (message.IMessageElement, error) {
|
func (bot *CQBot) makeImageOrVideoElem(elem msg.Element, video bool, sourceType message.SourceType) (message.IMessageElement, error) {
|
||||||
f := d["file"]
|
f := elem.Get("file")
|
||||||
u, ok := d["url"]
|
u := elem.Get("url")
|
||||||
if !ok {
|
|
||||||
u = ""
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(f, "http") {
|
if strings.HasPrefix(f, "http") {
|
||||||
hash := md5.Sum([]byte(f))
|
hash := md5.Sum([]byte(f))
|
||||||
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash[:])+".cache")
|
cacheFile := path.Join(global.CachePath, hex.EncodeToString(hash[:])+".cache")
|
||||||
@ -1073,9 +813,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy
|
|||||||
if video {
|
if video {
|
||||||
maxSize = maxVideoSize
|
maxSize = maxVideoSize
|
||||||
}
|
}
|
||||||
thread, _ := strconv.Atoi(d["c"])
|
thread, _ := strconv.Atoi(elem.Get("c"))
|
||||||
exist := global.PathExists(cacheFile)
|
exist := global.PathExists(cacheFile)
|
||||||
if exist && (d["cache"] == "" || d["cache"] == "1") {
|
if exist && (elem.Get("cache") == "" || elem.Get("cache") == "1") {
|
||||||
goto useCacheFile
|
goto useCacheFile
|
||||||
}
|
}
|
||||||
if exist {
|
if exist {
|
||||||
@ -1170,8 +910,9 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceTy
|
|||||||
}
|
}
|
||||||
exist := global.PathExists(rawPath)
|
exist := global.PathExists(rawPath)
|
||||||
if !exist {
|
if !exist {
|
||||||
if d["url"] != "" {
|
if elem.Get("url") != "" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, sourceType)
|
elem.Data = []msg.Pair{{K: "file", V: elem.Get("url")}}
|
||||||
|
return bot.makeImageOrVideoElem(elem, false, sourceType)
|
||||||
}
|
}
|
||||||
return nil, errors.New("invalid image")
|
return nil, errors.New("invalid image")
|
||||||
}
|
}
|
||||||
@ -1196,7 +937,11 @@ func (bot *CQBot) readImageCache(b []byte, sourceType message.SourceType) (messa
|
|||||||
r.ReadString()
|
r.ReadString()
|
||||||
imageURL := r.ReadString()
|
imageURL := r.ReadString()
|
||||||
if size == 0 && imageURL != "" {
|
if size == 0 && imageURL != "" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType)
|
// TODO: fix this
|
||||||
|
var elem msg.Element
|
||||||
|
elem.Type = "image"
|
||||||
|
elem.Data = []msg.Pair{{K: "file", V: imageURL}}
|
||||||
|
return bot.makeImageOrVideoElem(elem, false, sourceType)
|
||||||
}
|
}
|
||||||
var rsp message.IMessageElement
|
var rsp message.IMessageElement
|
||||||
switch sourceType { // nolint:exhaustive
|
switch sourceType { // nolint:exhaustive
|
||||||
@ -1213,7 +958,10 @@ func (bot *CQBot) readImageCache(b []byte, sourceType message.SourceType) (messa
|
|||||||
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
|
||||||
}
|
}
|
||||||
if err != nil && imageURL != "" {
|
if err != nil && imageURL != "" {
|
||||||
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType)
|
var elem msg.Element
|
||||||
|
elem.Type = "image"
|
||||||
|
elem.Data = []msg.Pair{{K: "file", V: imageURL}}
|
||||||
|
return bot.makeImageOrVideoElem(elem, false, sourceType)
|
||||||
}
|
}
|
||||||
return rsp, err
|
return rsp, err
|
||||||
}
|
}
|
||||||
|
@ -88,16 +88,26 @@ func UnescapeValue(content string) string {
|
|||||||
|
|
||||||
// @@@ 消息中间表示 @@@
|
// @@@ 消息中间表示 @@@
|
||||||
|
|
||||||
|
// Pair key value pair
|
||||||
|
type Pair struct {
|
||||||
|
K string
|
||||||
|
V string
|
||||||
|
}
|
||||||
|
|
||||||
// Element single message
|
// Element single message
|
||||||
type Element struct {
|
type Element struct {
|
||||||
Type string
|
Type string
|
||||||
Data []Pair
|
Data []Pair
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pair key value pair
|
// Get 获取指定值
|
||||||
type Pair struct {
|
func (e *Element) Get(k string) string {
|
||||||
K string
|
for _, datum := range e.Data {
|
||||||
V string
|
if datum.K == k {
|
||||||
|
return datum.V
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// CQCode convert element to cqcode
|
// CQCode convert element to cqcode
|
||||||
@ -140,7 +150,7 @@ func (e *Element) MarshalJSON() ([]byte, error) {
|
|||||||
buf.WriteByte('"')
|
buf.WriteByte('"')
|
||||||
buf.WriteString(data.K)
|
buf.WriteString(data.K)
|
||||||
buf.WriteString(`":`)
|
buf.WriteString(`":`)
|
||||||
writeQuote(buf, data.V)
|
buf.WriteString(QuoteJSON(data.V))
|
||||||
}
|
}
|
||||||
buf.WriteString(`}}`)
|
buf.WriteString(`}}`)
|
||||||
}), nil
|
}), nil
|
||||||
@ -148,9 +158,10 @@ func (e *Element) MarshalJSON() ([]byte, error) {
|
|||||||
|
|
||||||
const hex = "0123456789abcdef"
|
const hex = "0123456789abcdef"
|
||||||
|
|
||||||
func writeQuote(b *bytes.Buffer, s string) {
|
// QuoteJSON 按JSON转义为字符加上双引号
|
||||||
|
func QuoteJSON(s string) string {
|
||||||
i, j := 0, 0
|
i, j := 0, 0
|
||||||
|
var b strings.Builder
|
||||||
b.WriteByte('"')
|
b.WriteByte('"')
|
||||||
for j < len(s) {
|
for j < len(s) {
|
||||||
c := s[j]
|
c := s[j]
|
||||||
@ -231,4 +242,5 @@ func writeQuote(b *bytes.Buffer, s string) {
|
|||||||
|
|
||||||
b.WriteString(s[i:])
|
b.WriteString(s[i:])
|
||||||
b.WriteByte('"')
|
b.WriteByte('"')
|
||||||
|
return b.String()
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package msg
|
package msg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -14,16 +13,14 @@ func jsonMarshal(s string) string {
|
|||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_quote(t *testing.T) {
|
func TestQuoteJSON(t *testing.T) {
|
||||||
testcase := []string{
|
testcase := []string{
|
||||||
"\u0005", // issue 1773
|
"\u0005", // issue 1773
|
||||||
"\v",
|
"\v",
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, input := range testcase {
|
for _, input := range testcase {
|
||||||
var b bytes.Buffer
|
got := QuoteJSON(input)
|
||||||
writeQuote(&b, input)
|
|
||||||
got := b.String()
|
|
||||||
expected := jsonMarshal(input)
|
expected := jsonMarshal(input)
|
||||||
if got != expected {
|
if got != expected {
|
||||||
t.Errorf("want %v but got %v", expected, got)
|
t.Errorf("want %v but got %v", expected, got)
|
||||||
|
104
internal/msg/parse.go
Normal file
104
internal/msg/parse.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseObject 将消息JSON对象转为消息元素数组
|
||||||
|
func ParseObject(m gjson.Result) (r []Element) {
|
||||||
|
convert := func(e gjson.Result) {
|
||||||
|
var elem Element
|
||||||
|
elem.Type = e.Get("type").Str
|
||||||
|
e.Get("data").ForEach(func(key, value gjson.Result) bool {
|
||||||
|
pair := Pair{K: key.Str, V: value.String()}
|
||||||
|
elem.Data = append(elem.Data, pair)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
r = append(r, elem)
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.IsArray() {
|
||||||
|
m.ForEach(func(_, e gjson.Result) bool {
|
||||||
|
convert(e)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if m.IsObject() {
|
||||||
|
convert(m)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func text(txt string) Element {
|
||||||
|
return Element{
|
||||||
|
Type: "text",
|
||||||
|
Data: []Pair{
|
||||||
|
{
|
||||||
|
K: "text",
|
||||||
|
V: txt,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseString 将字符串(CQ码)转为消息元素数组
|
||||||
|
func ParseString(raw string) (r []Element) {
|
||||||
|
var elem Element
|
||||||
|
for raw != "" {
|
||||||
|
i := 0
|
||||||
|
for i < len(raw) && !(raw[i] == '[' && i+4 < len(raw) && raw[i:i+4] == "[CQ:") {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i > 0 {
|
||||||
|
r = append(r, text(UnescapeText(raw[:i])))
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+4 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw = raw[i+4:] // skip "[CQ:"
|
||||||
|
i = 0
|
||||||
|
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elem.Type = raw[:i]
|
||||||
|
elem.Data = nil // reset data
|
||||||
|
raw = raw[i:]
|
||||||
|
i = 0
|
||||||
|
for {
|
||||||
|
if raw[0] == ']' {
|
||||||
|
r = append(r, elem)
|
||||||
|
raw = raw[1:]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
raw = raw[1:]
|
||||||
|
|
||||||
|
for i < len(raw) && raw[i] != '=' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
key := raw[:i]
|
||||||
|
raw = raw[i+1:] // skip "="
|
||||||
|
i = 0
|
||||||
|
for i < len(raw) && raw[i] != ',' && raw[i] != ']' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i+1 > len(raw) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elem.Data = append(elem.Data, Pair{
|
||||||
|
K: key,
|
||||||
|
V: UnescapeValue(raw[:i]),
|
||||||
|
})
|
||||||
|
raw = raw[i:]
|
||||||
|
i = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -1,22 +1,18 @@
|
|||||||
package coolq
|
package msg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Mrs4s/MiraiGo/message"
|
|
||||||
"github.com/Mrs4s/MiraiGo/utils"
|
"github.com/Mrs4s/MiraiGo/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
|
|
||||||
"github.com/Mrs4s/go-cqhttp/internal/msg"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var bot = CQBot{}
|
func TestParseString(t *testing.T) {
|
||||||
|
// TODO: add more text
|
||||||
func TestCQBot_ConvertStringMessage(t *testing.T) {
|
for _, v := range ParseString(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`) {
|
||||||
for _, v := range bot.ConvertStringMessage(`[CQ:face,id=115,text=111][CQ:face,id=217]] [CQ:text,text=123] [`, message.SourcePrivate) {
|
|
||||||
fmt.Println(v)
|
fmt.Println(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -26,17 +22,18 @@ var (
|
|||||||
benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`)
|
benchArray = gjson.Parse(`[{"type":"text","data":{"text":"asdfqwerqwerqwer"}},{"type":"face","data":{"id":"115","text":"111"}},{"type":"text","data":{"text":"asdfasdfasdfasdfasdfasdfasd"}},{"type":"face","data":{"id":"217"}},{"type":"text","data":{"text":"] "}},{"type":"text","data":{"text":"123"}},{"type":"text","data":{"text":" ["}}]`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkCQBot_ConvertStringMessage(b *testing.B) {
|
func BenchmarkParseString(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bot.ConvertStringMessage(bench, message.SourcePrivate)
|
ParseString(bench)
|
||||||
}
|
}
|
||||||
b.SetBytes(int64(len(bench)))
|
b.SetBytes(int64(len(bench)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCQBot_ConvertObjectMessage(b *testing.B) {
|
func BenchmarkParseObject(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
bot.ConvertObjectMessage(benchArray, message.SourcePrivate)
|
ParseObject(benchArray)
|
||||||
}
|
}
|
||||||
|
b.SetBytes(int64(len(benchArray.Raw)))
|
||||||
}
|
}
|
||||||
|
|
||||||
const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&987654321[]&`
|
const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&987654321[]&`
|
||||||
@ -44,16 +41,7 @@ const bText = `123456789[]&987654321[]&987654321[]&987654321[]&987654321[]&98765
|
|||||||
func BenchmarkCQCodeEscapeText(b *testing.B) {
|
func BenchmarkCQCodeEscapeText(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
ret := bText
|
ret := bText
|
||||||
msg.EscapeText(ret)
|
EscapeText(ret)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCQCodeEscapeTextBefore(b *testing.B) {
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
ret := bText
|
|
||||||
ret = strings.ReplaceAll(ret, "&", "&")
|
|
||||||
ret = strings.ReplaceAll(ret, "[", "[")
|
|
||||||
strings.ReplaceAll(ret, "]", "]")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +52,6 @@ func TestCQCodeEscapeText(t *testing.T) {
|
|||||||
ret = strings.ReplaceAll(ret, "&", "&")
|
ret = strings.ReplaceAll(ret, "&", "&")
|
||||||
ret = strings.ReplaceAll(ret, "[", "[")
|
ret = strings.ReplaceAll(ret, "[", "[")
|
||||||
ret = strings.ReplaceAll(ret, "]", "]")
|
ret = strings.ReplaceAll(ret, "]", "]")
|
||||||
assert.Equal(t, ret, msg.EscapeText(rs))
|
assert.Equal(t, ret, EscapeText(rs))
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user