1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-05 03:23:49 +08:00

feat: support parse multi-source message & support parse and forward guild image message

This commit is contained in:
Mrs4s 2021-11-13 03:08:41 +08:00
parent 4d328358e3
commit 52e7ea5bbe
No known key found for this signature in database
GPG Key ID: 3186E98FA19CE3A7
8 changed files with 200 additions and 138 deletions

View File

@ -540,7 +540,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b
var elem []message.IMessageElement var elem []message.IMessageElement
if m.Type == gjson.JSON { if m.Type == gjson.JSON {
elem = bot.ConvertObjectMessage(m, true) elem = bot.ConvertObjectMessage(m, MessageSourceGroup)
} else { } else {
str := m.String() str := m.String()
if str == "" { if str == "" {
@ -550,7 +550,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b
if autoEscape { if autoEscape {
elem = []message.IMessageElement{message.NewText(str)} elem = []message.IMessageElement{message.NewText(str)}
} else { } else {
elem = bot.ConvertStringMessage(str, true) elem = bot.ConvertStringMessage(str, MessageSourceGroup)
} }
} }
fixAt(elem) fixAt(elem)
@ -580,9 +580,8 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
return Failed(100, "CHANNEL_NOT_SUPPORTED_TEXT_MSG", "子频道类型错误, 无法发送文本信息") return Failed(100, "CHANNEL_NOT_SUPPORTED_TEXT_MSG", "子频道类型错误, 无法发送文本信息")
} }
var elem []message.IMessageElement var elem []message.IMessageElement
// todo: 将 converter 适配频道或者为频道单独写一个
if m.Type == gjson.JSON { if m.Type == gjson.JSON {
elem = bot.ConvertObjectMessage(m, true) elem = bot.ConvertObjectMessage(m, MessageSourceGuildChannel)
} else { } else {
str := m.String() str := m.String()
if str == "" { if str == "" {
@ -592,7 +591,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
if autoEscape { if autoEscape {
elem = []message.IMessageElement{message.NewText(str)} elem = []message.IMessageElement{message.NewText(str)}
} else { } else {
elem = bot.ConvertStringMessage(str, true) elem = bot.ConvertStringMessage(str, MessageSourceGuildChannel)
} }
} }
mid := bot.SendGuildChannelMessage(guildID, channelID, &message.SendingMessage{Elements: elem}) 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) 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) 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 { if uin != 0 && name != "" && len(content) > 0 {
return &message.ForwardNode{ return &message.ForwardNode{
SenderId: uin, 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 { func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG {
var elem []message.IMessageElement var elem []message.IMessageElement
if m.Type == gjson.JSON { if m.Type == gjson.JSON {
elem = bot.ConvertObjectMessage(m, false) elem = bot.ConvertObjectMessage(m, MessageSourcePrivate)
} else { } else {
str := m.String() str := m.String()
if str == "" { if str == "" {
@ -753,7 +752,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Resu
if autoEscape { if autoEscape {
elem = []message.IMessageElement{message.NewText(str)} elem = []message.IMessageElement{message.NewText(str)}
} else { } else {
elem = bot.ConvertStringMessage(str, false) elem = bot.ConvertStringMessage(str, MessageSourcePrivate)
} }
} }
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem}) mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
@ -1339,7 +1338,7 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG {
"nickname": n.SenderName, "nickname": n.SenderName,
}, },
"time": n.Time, "time": n.Time,
"content": ToFormattedMessage(n.Message, 0, false), "content": ToFormattedMessage(n.Message, MessageSource{SourceType: MessageSourceGroup}, false),
}) })
} }
return OK(global.MSG{ return OK(global.MSG{
@ -1373,9 +1372,9 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG {
switch o := msg.(type) { switch o := msg.(type) {
case *db.StoredGroupMessage: case *db.StoredGroupMessage:
m["group_id"] = o.GroupCode 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: 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) return OK(m)
} }
@ -1474,7 +1473,7 @@ func (bot *CQBot) CQCanSendRecord() global.MSG {
// @alias(.ocr_image) // @alias(.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, true) img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, MessageSourceGroup)
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())

View File

@ -523,76 +523,6 @@ func (bot *CQBot) dispatchEventMessage(m global.MSG) {
global.PutBuffer(event.buffer) 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 { func formatGroupName(group *client.GroupInfo) string {
return fmt.Sprintf("%s(%d)", group.Name, group.Code) return fmt.Sprintf("%s(%d)", group.Name, group.Code)
} }

View File

@ -2,7 +2,9 @@ package coolq
import ( import (
"github.com/Mrs4s/MiraiGo/client" "github.com/Mrs4s/MiraiGo/client"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/go-cqhttp/global" "github.com/Mrs4s/go-cqhttp/global"
log "github.com/sirupsen/logrus"
"strconv" "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 { func convertChannelInfo(c *client.ChannelInfo) global.MSG {
slowModes := make([]global.MSG, 0, len(c.Meta.SlowModes)) slowModes := make([]global.MSG, 0, len(c.Meta.SlowModes))
for _, mode := range c.Meta.SlowModes { for _, mode := range c.Meta.SlowModes {

View File

@ -36,11 +36,6 @@ var typeReg = regexp.MustCompile(`\[CQ:(\w+)`)
var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`) var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`)
*/ */
const (
maxImageSize = 1024 * 1024 * 30 // 30MB
maxVideoSize = 1024 * 1024 * 100 // 100MB
)
// PokeElement 拍一拍 // PokeElement 拍一拍
type PokeElement struct { type PokeElement struct {
Target int64 Target int64
@ -73,6 +68,27 @@ type LocalVideoElement struct {
thumb io.ReadSeeker 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. // Type implements the message.IMessageElement.
func (e *LocalImageElement) Type() message.ElementType { func (e *LocalImageElement) Type() message.ElementType {
return message.Image return message.Image
@ -114,16 +130,16 @@ func (e *PokeElement) Type() message.ElementType {
} }
// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报 // 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)) r = make([]global.MSG, 0, len(e))
m := &message.SendingMessage{Elements: e} m := &message.SendingMessage{Elements: e}
reply := m.FirstOrNil(func(e message.IMessageElement) bool { reply := m.FirstOrNil(func(e message.IMessageElement) bool {
_, ok := e.(*message.ReplyElement) _, ok := e.(*message.ReplyElement)
return ok return ok
}) })
if reply != nil { if reply != nil && source.SourceType == MessageSourceGroup {
replyElem := reply.(*message.ReplyElement) replyElem := reply.(*message.ReplyElement)
rid := groupID rid := int64(source.PrimaryID)
if rid == 0 { if rid == 0 {
rid = replyElem.Sender rid = replyElem.Sender
} }
@ -138,7 +154,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG)
"seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10), "seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10),
"qq": strconv.FormatInt(replyElem.Sender, 10), "qq": strconv.FormatInt(replyElem.Sender, 10),
"time": strconv.FormatInt(int64(replyElem.Time), 10), "time": strconv.FormatInt(int64(replyElem.Time), 10),
"text": ToStringMessage(replyElem.Elements, groupID), "text": ToStringMessage(replyElem.Elements, source),
}, },
}) })
} else { } else {
@ -260,7 +276,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG)
} }
// ToStringMessage 将消息元素数组转为字符串以用于消息上报 // 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 := global.NewBuffer()
sb.Reset() sb.Reset()
write := func(format string, a ...interface{}) { write := func(format string, a ...interface{}) {
@ -276,9 +292,9 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
_, ok := e.(*message.ReplyElement) _, ok := e.(*message.ReplyElement)
return ok return ok
}) })
if reply != nil { if reply != nil && source.SourceType == MessageSourceGroup {
replyElem := reply.(*message.ReplyElement) replyElem := reply.(*message.ReplyElement)
rid := groupID rid := int64(source.PrimaryID)
if rid == 0 { if rid == 0 {
rid = replyElem.Sender 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]", write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
db.ToGlobalID(rid, replyElem.ReplySeq), db.ToGlobalID(rid, replyElem.ReplySeq),
replyElem.ReplySeq, replyElem.Sender, replyElem.Time, replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
CQCodeEscapeValue(ToStringMessage(replyElem.Elements, groupID))) CQCodeEscapeValue(ToStringMessage(replyElem.Elements, source)))
} else { } else {
write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq)) write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq))
} }
@ -352,6 +368,8 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
} else { } else {
write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url), arg) 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: case *message.DiceElement:
write("[CQ:dice,value=%v]", o.Value) write("[CQ:dice,value=%v]", o.Value)
case *message.MarketFaceElement: case *message.MarketFaceElement:
@ -480,7 +498,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) {
} }
// ConvertStringMessage 将消息字符串转为消息元素数组 // 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 var t, key string
d := map[string]string{} d := map[string]string{}
@ -516,7 +534,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
ReplySeq: org.GetAttribute().MessageSeq, ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin, Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp), Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertStringMessage(customText, isGroup), Elements: bot.ConvertStringMessage(customText, sourceType),
} }
if senderErr != nil { if senderErr != nil {
elem.Sender = sender elem.Sender = sender
@ -532,7 +550,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
ReplySeq: int32(messageSeq), ReplySeq: int32(messageSeq),
Sender: sender, Sender: sender,
Time: int32(msgTime), Time: int32(msgTime),
Elements: bot.ConvertStringMessage(customText, isGroup), Elements: bot.ConvertStringMessage(customText, sourceType),
} }
} }
r = append([]message.IMessageElement{elem}, r...) 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, ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin, Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp), Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertContentMessage(org.GetContent(), isGroup), Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
}, },
}, r...) }, r...)
} }
@ -565,7 +583,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
} }
return return
} }
elem, err := bot.ToElement(t, d, isGroup) elem, err := bot.ToElement(t, d, sourceType)
if err != nil { if err != nil {
org := "[CQ:" + t org := "[CQ:" + t
for k, v := range d { for k, v := range d {
@ -653,11 +671,11 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
} }
// ConvertObjectMessage 将消息JSON对象转为消息元素数组 // 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) d := make(map[string]string)
convertElem := func(e gjson.Result) { convertElem := func(e gjson.Result) {
t := e.Get("type").Str t := e.Get("type").Str
if t == "reply" && isGroup { if t == "reply" && sourceType == MessageSourceGroup {
if len(r) > 0 { if len(r) > 0 {
if _, ok := r[0].(*message.ReplyElement); ok { if _, ok := r[0].(*message.ReplyElement); ok {
log.Warnf("警告: 一条信息只能包含一个 Reply 元素.") log.Warnf("警告: 一条信息只能包含一个 Reply 元素.")
@ -688,7 +706,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
ReplySeq: org.GetAttribute().MessageSeq, ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin, Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp), Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertStringMessage(customText, isGroup), Elements: bot.ConvertStringMessage(customText, sourceType),
} }
if senderErr != nil { if senderErr != nil {
elem.Sender = sender elem.Sender = sender
@ -704,7 +722,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
ReplySeq: int32(messageSeq), ReplySeq: int32(messageSeq),
Sender: sender, Sender: sender,
Time: int32(msgTime), Time: int32(msgTime),
Elements: bot.ConvertStringMessage(customText, isGroup), Elements: bot.ConvertStringMessage(customText, sourceType),
} }
} }
r = append([]message.IMessageElement{elem}, r...) 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, ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin, Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp), Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertContentMessage(org.GetContent(), isGroup), Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
}, },
}, r...) }, r...)
} }
@ -745,7 +763,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
d[key.Str] = value.String() d[key.Str] = value.String()
return true return true
}) })
elem, err := bot.ToElement(t, d, isGroup) elem, err := bot.ToElement(t, d, sourceType)
if err != nil { if err != nil {
log.Warnf("转换CQ码 (%v) 到MiraiGo Element时出现错误: %v 将忽略本段CQ码.", e.Raw, err) log.Warnf("转换CQ码 (%v) 到MiraiGo Element时出现错误: %v 将忽略本段CQ码.", e.Raw, err)
return return
@ -758,7 +776,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
} }
} }
if m.Type == gjson.String { if m.Type == gjson.String {
return bot.ConvertStringMessage(m.Str, isGroup) return bot.ConvertStringMessage(m.Str, sourceType)
} }
if m.IsArray() { if m.IsArray() {
m.ForEach(func(_, e gjson.Result) bool { m.ForEach(func(_, e gjson.Result) bool {
@ -773,14 +791,14 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
} }
// ConvertContentMessage 将数据库用的 content 转换为消息元素数组 // 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 { for _, c := range content {
data := c["data"].(global.MSG) data := c["data"].(global.MSG)
switch c["type"] { switch c["type"] {
case "text": case "text":
r = append(r, message.NewText(data["text"].(string))) r = append(r, message.NewText(data["text"].(string)))
case "image": 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 { if err != nil {
log.Warnf("make image elem error: %v", err) log.Warnf("make image elem error: %v", err)
continue continue
@ -830,7 +848,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []m
case "face": case "face":
r = append(r, message.NewFace(data["id"].(int32))) r = append(r, message.NewFace(data["id"].(int32)))
case "video": 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 { if err != nil {
log.Warnf("make image elem error: %v", err) log.Warnf("make image elem error: %v", err)
continue continue
@ -846,7 +864,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []m
// 返回 interface{} 存在三种类型 // 返回 interface{} 存在三种类型
// //
// message.IMessageElement []message.IMessageElement nil // 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 { switch t {
case "text": case "text":
if base.SplitURL { 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 return message.NewText(d["text"]), nil
case "image": case "image":
img, err := bot.makeImageOrVideoElem(d, false, isGroup) img, err := bot.makeImageOrVideoElem(d, false, sourceType)
if err != nil { if err != nil {
return nil, err 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) t, _ := strconv.ParseInt(d["qq"], 10, 64)
return &PokeElement{Target: t}, nil return &PokeElement{Target: t}, nil
case "gift": case "gift":
if !isGroup { if sourceType != MessageSourceGroup {
return nil, errors.New("private gift unsupported") // no free private gift return nil, errors.New("private gift unsupported") // no free private gift
} }
t, _ := strconv.ParseInt(d["qq"], 10, 64) 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) 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, isGroup) img, err := bot.makeImageOrVideoElem(d, 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, isGroup) return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == MessageSourceGroup)
case "video": case "video":
file, err := bot.makeImageOrVideoElem(d, true, isGroup) file, err := bot.makeImageOrVideoElem(d, true, sourceType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1248,7 +1266,7 @@ func CQCodeUnescapeValue(content string) string {
} }
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用 // 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"] f := d["file"]
if strings.HasPrefix(f, "http") { if strings.HasPrefix(f, "http") {
hash := md5.Sum([]byte(f)) hash := md5.Sum([]byte(f))
@ -1327,11 +1345,18 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
b, _ := os.ReadFile(rawPath) b, _ := os.ReadFile(rawPath)
return bot.readVideoCache(b), nil 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 { if strings.HasSuffix(f, ".image") && cache.EnableCacheDB {
hash, err := hex.DecodeString(strings.TrimSuffix(f, ".image")) hash, err := hex.DecodeString(strings.TrimSuffix(f, ".image"))
if err == nil { if err == nil {
if b := cache.Image.Get(hash); b != 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 !exist {
if d["url"] != "" { 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") return nil, errors.New("invalid image")
} }
@ -1353,10 +1378,10 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
if err != nil { if err != nil {
return nil, err 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 var err error
if len(b) < 20 { if len(b) < 20 {
return nil, errors.New("invalid cache") return nil, errors.New("invalid cache")
@ -1368,7 +1393,7 @@ func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement,
imageURL := r.ReadString() imageURL := r.ReadString()
if size == 0 { if size == 0 {
if imageURL != "" { 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") 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") return nil, errors.New("invalid hash")
} }
var rsp message.IMessageElement var rsp message.IMessageElement
if group { if sourceType == MessageSourceGroup {
rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size) rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size)
goto ok 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) rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
ok: ok:
if err != nil { if err != nil {
if imageURL != "" { 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 return nil, err
} }

View File

@ -22,18 +22,22 @@ import (
) )
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式 // 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" { if base.PostFormat == "string" {
r = ToStringMessage(e, groupID, isRaw...) r = ToStringMessage(e, source, isRaw...)
} else if base.PostFormat == "array" { } else if base.PostFormat == "array" {
r = ToArrayMessage(e, groupID) r = ToArrayMessage(e, source)
} }
return return
} }
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) { func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
bot.checkMedia(m.Elements, m.Sender.Uin) 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) id := bot.InsertPrivateMessage(m)
log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id) log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
fm := global.MSG{ fm := global.MSG{
@ -48,7 +52,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
"message_id": id, "message_id": id,
"user_id": m.Sender.Uin, "user_id": m.Sender.Uin,
"target_id": m.Target, "target_id": m.Target,
"message": ToFormattedMessage(m.Elements, 0, false), "message": ToFormattedMessage(m.Elements, source, false),
"raw_message": cqm, "raw_message": cqm,
"font": 0, "font": 0,
"self_id": c.Uin, "self_id": c.Uin,
@ -86,7 +90,11 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
return 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) id := bot.InsertGroupMessage(m)
log.Infof("收到群 %v(%v) 内 %v(%v) 的消息: %v (%v)", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm, id) log.Infof("收到群 %v(%v) 内 %v(%v) 的消息: %v (%v)", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
gm := bot.formatGroupMessage(m) 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) { func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
m := e.Message m := e.Message
bot.checkMedia(m.Elements, m.Sender.Uin) 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) bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
id := m.Id id := m.Id
// todo(Mrs4s) // todo(Mrs4s)
@ -115,7 +127,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
"temp_source": e.Session.Source, "temp_source": e.Session.Source,
"message_id": id, "message_id": id,
"user_id": m.Sender.Uin, "user_id": m.Sender.Uin,
"message": ToFormattedMessage(m.Elements, 0, false), "message": ToFormattedMessage(m.Elements, source, false),
"raw_message": cqm, "raw_message": cqm,
"font": 0, "font": 0,
"self_id": c.Uin, "self_id": c.Uin,
@ -143,7 +155,12 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC
channel = c 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: 数据库支持 // todo: 数据库支持
bot.dispatchEventMessage(global.MSG{ bot.dispatchEventMessage(global.MSG{
"post_type": "message", "post_type": "message",
@ -153,7 +170,7 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC
"channel_id": m.ChannelId, "channel_id": m.ChannelId,
"message_id": fmt.Sprintf("%v-%v", m.Id, m.InternalId), "message_id": fmt.Sprintf("%v-%v", m.Id, m.InternalId),
"user_id": m.Sender.TinyId, "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_id": bot.Client.Uin,
"self_tiny_id": bot.Client.GuildService.TinyId, "self_tiny_id": bot.Client.GuildService.TinyId,
"time": m.Time, "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)) { } else if !global.PathExists(path.Join(global.ImagePath, filename)) {
_ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644) _ = 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: case *message.FriendImageElement:
data := binary.NewWriterF(func(w *binary.Writer) { data := binary.NewWriterF(func(w *binary.Writer) {
w.Write(i.Md5) w.Write(i.Md5)

2
go.mod
View File

@ -7,7 +7,7 @@ replace github.com/willf/bitset v1.2.0 => github.com/bits-and-blooms/bitset v1.2
require ( require (
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Microsoft/go-winio v0.5.1 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/dustin/go-humanize v1.0.0
github.com/fumiama/go-hide-param v0.1.4 github.com/fumiama/go-hide-param v0.1.4
github.com/gabriel-vasile/mimetype v1.4.0 github.com/gabriel-vasile/mimetype v1.4.0

4
go.sum
View File

@ -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/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 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY=
github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= 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-20211112183036-d3a21e577b02 h1:D2a93R7eugdAfuZtVXYznjqGrKlfvtWQ0YTHnwJYq8U=
github.com/Mrs4s/MiraiGo v0.0.0-20211111181749-b85fc25cd59b/go.mod h1:imVKbfKqqeit+C/eaWGb4MKQ3z3gN6pRpBU5RMtp5so= 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 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= 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= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=

View File

@ -84,6 +84,7 @@ func main() {
mkCacheDir(global.VoicePath, "语音") mkCacheDir(global.VoicePath, "语音")
mkCacheDir(global.VideoPath, "视频") mkCacheDir(global.VideoPath, "视频")
mkCacheDir(global.CachePath, "发送图片") mkCacheDir(global.CachePath, "发送图片")
mkCacheDir(path.Join(global.ImagePath, "guild-images"), "频道图片缓存")
cache.Init() cache.Init()
db.Init() db.Init()