1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-04 19:17:37 +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
if m.Type == gjson.JSON {
elem = bot.ConvertObjectMessage(m, true)
elem = bot.ConvertObjectMessage(m, MessageSourceGroup)
} else {
str := m.String()
if str == "" {
@ -550,7 +550,7 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b
if autoEscape {
elem = []message.IMessageElement{message.NewText(str)}
} else {
elem = bot.ConvertStringMessage(str, true)
elem = bot.ConvertStringMessage(str, MessageSourceGroup)
}
}
fixAt(elem)
@ -580,9 +580,8 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
return Failed(100, "CHANNEL_NOT_SUPPORTED_TEXT_MSG", "子频道类型错误, 无法发送文本信息")
}
var elem []message.IMessageElement
// todo: 将 converter 适配频道或者为频道单独写一个
if m.Type == gjson.JSON {
elem = bot.ConvertObjectMessage(m, true)
elem = bot.ConvertObjectMessage(m, MessageSourceGuildChannel)
} else {
str := m.String()
if str == "" {
@ -592,7 +591,7 @@ func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.R
if autoEscape {
elem = []message.IMessageElement{message.NewText(str)}
} else {
elem = bot.ConvertStringMessage(str, true)
elem = bot.ConvertStringMessage(str, MessageSourceGuildChannel)
}
}
mid := bot.SendGuildChannelMessage(guildID, channelID, &message.SendingMessage{Elements: elem})
@ -658,7 +657,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa
}
return int32(msgTime)
}(),
Message: resolveElement(bot.ConvertContentMessage(m.Content, true)),
Message: resolveElement(bot.ConvertContentMessage(m.Content, MessageSourceGroup)),
}
}
log.Warnf("警告: 引用消息 %v 错误或数据库未开启.", e.Get("data.id").Str)
@ -697,7 +696,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa
}
}
}
content := bot.ConvertObjectMessage(e.Get("data.content"), true)
content := bot.ConvertObjectMessage(e.Get("data.content"), MessageSourceGroup)
if uin != 0 && name != "" && len(content) > 0 {
return &message.ForwardNode{
SenderId: uin,
@ -744,7 +743,7 @@ func (bot *CQBot) CQSendGroupForwardMessage(groupID int64, m gjson.Result) globa
func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Result, autoEscape bool) global.MSG {
var elem []message.IMessageElement
if m.Type == gjson.JSON {
elem = bot.ConvertObjectMessage(m, false)
elem = bot.ConvertObjectMessage(m, MessageSourcePrivate)
} else {
str := m.String()
if str == "" {
@ -753,7 +752,7 @@ func (bot *CQBot) CQSendPrivateMessage(userID int64, groupID int64, m gjson.Resu
if autoEscape {
elem = []message.IMessageElement{message.NewText(str)}
} else {
elem = bot.ConvertStringMessage(str, false)
elem = bot.ConvertStringMessage(str, MessageSourcePrivate)
}
}
mid := bot.SendPrivateMessage(userID, groupID, &message.SendingMessage{Elements: elem})
@ -1339,7 +1338,7 @@ func (bot *CQBot) CQGetForwardMessage(resID string) global.MSG {
"nickname": n.SenderName,
},
"time": n.Time,
"content": ToFormattedMessage(n.Message, 0, false),
"content": ToFormattedMessage(n.Message, MessageSource{SourceType: MessageSourceGroup}, false),
})
}
return OK(global.MSG{
@ -1373,9 +1372,9 @@ func (bot *CQBot) CQGetMessage(messageID int32) global.MSG {
switch o := msg.(type) {
case *db.StoredGroupMessage:
m["group_id"] = o.GroupCode
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, true), o.GroupCode, false)
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, MessageSourceGroup), MessageSource{SourceType: MessageSourceGroup, PrimaryID: uint64(o.GroupCode)}, false)
case *db.StoredPrivateMessage:
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, false), 0, false)
m["message"] = ToFormattedMessage(bot.ConvertContentMessage(o.Content, MessageSourcePrivate), MessageSource{SourceType: MessageSourcePrivate}, false)
}
return OK(m)
}
@ -1474,7 +1473,7 @@ func (bot *CQBot) CQCanSendRecord() global.MSG {
// @alias(.ocr_image)
// @rename(image_id->image)
func (bot *CQBot) CQOcrImage(imageID string) global.MSG {
img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, true)
img, err := bot.makeImageOrVideoElem(map[string]string{"file": imageID}, false, MessageSourceGroup)
if err != nil {
log.Warnf("load image error: %v", err)
return Failed(100, "LOAD_FILE_ERROR", err.Error())

View File

@ -523,76 +523,6 @@ func (bot *CQBot) dispatchEventMessage(m global.MSG) {
global.PutBuffer(event.buffer)
}
func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG {
cqm := ToStringMessage(m.Elements, m.GroupCode, true)
gm := global.MSG{
"anonymous": nil,
"font": 0,
"group_id": m.GroupCode,
"message": ToFormattedMessage(m.Elements, m.GroupCode, false),
"message_type": "group",
"message_seq": m.Id,
"post_type": func() string {
if m.Sender.Uin == bot.Client.Uin {
return "message_sent"
}
return "message"
}(),
"raw_message": cqm,
"self_id": bot.Client.Uin,
"sender": global.MSG{
"age": 0,
"area": "",
"level": "",
"sex": "unknown",
"user_id": m.Sender.Uin,
},
"sub_type": "normal",
"time": m.Time,
"user_id": m.Sender.Uin,
}
if m.Sender.IsAnonymous() {
gm["anonymous"] = global.MSG{
"flag": m.Sender.AnonymousInfo.AnonymousId + "|" + m.Sender.AnonymousInfo.AnonymousNick,
"id": m.Sender.Uin,
"name": m.Sender.AnonymousInfo.AnonymousNick,
}
gm["sender"].(global.MSG)["nickname"] = "匿名消息"
gm["sub_type"] = "anonymous"
} else {
group := bot.Client.FindGroup(m.GroupCode)
mem := group.FindMember(m.Sender.Uin)
if mem == nil {
log.Warnf("获取 %v 成员信息失败,尝试刷新成员列表", m.Sender.Uin)
t, err := bot.Client.GetGroupMembers(group)
if err != nil {
log.Warnf("刷新群 %v 成员列表失败: %v", group.Uin, err)
return nil
}
group.Members = t
mem = group.FindMember(m.Sender.Uin)
if mem == nil {
return nil
}
}
ms := gm["sender"].(global.MSG)
switch mem.Permission {
case client.Owner:
ms["role"] = "owner"
case client.Administrator:
ms["role"] = "admin"
case client.Member:
ms["role"] = "member"
default:
ms["role"] = "member"
}
ms["nickname"] = mem.Nickname
ms["card"] = mem.CardName
ms["title"] = mem.SpecialTitle
}
return gm
}
func formatGroupName(group *client.GroupInfo) string {
return fmt.Sprintf("%s(%d)", group.Name, group.Code)
}

View File

@ -2,7 +2,9 @@ package coolq
import (
"github.com/Mrs4s/MiraiGo/client"
"github.com/Mrs4s/MiraiGo/message"
"github.com/Mrs4s/go-cqhttp/global"
log "github.com/sirupsen/logrus"
"strconv"
)
@ -55,6 +57,80 @@ func convertGuildMemberInfo(m *client.GuildMemberInfo) global.MSG {
}
}
func (bot *CQBot) formatGroupMessage(m *message.GroupMessage) global.MSG {
source := MessageSource{
SourceType: MessageSourceGroup,
PrimaryID: uint64(m.GroupCode),
}
cqm := ToStringMessage(m.Elements, source, true)
gm := global.MSG{
"anonymous": nil,
"font": 0,
"group_id": m.GroupCode,
"message": ToFormattedMessage(m.Elements, source, false),
"message_type": "group",
"message_seq": m.Id,
"post_type": func() string {
if m.Sender.Uin == bot.Client.Uin {
return "message_sent"
}
return "message"
}(),
"raw_message": cqm,
"self_id": bot.Client.Uin,
"sender": global.MSG{
"age": 0,
"area": "",
"level": "",
"sex": "unknown",
"user_id": m.Sender.Uin,
},
"sub_type": "normal",
"time": m.Time,
"user_id": m.Sender.Uin,
}
if m.Sender.IsAnonymous() {
gm["anonymous"] = global.MSG{
"flag": m.Sender.AnonymousInfo.AnonymousId + "|" + m.Sender.AnonymousInfo.AnonymousNick,
"id": m.Sender.Uin,
"name": m.Sender.AnonymousInfo.AnonymousNick,
}
gm["sender"].(global.MSG)["nickname"] = "匿名消息"
gm["sub_type"] = "anonymous"
} else {
group := bot.Client.FindGroup(m.GroupCode)
mem := group.FindMember(m.Sender.Uin)
if mem == nil {
log.Warnf("获取 %v 成员信息失败,尝试刷新成员列表", m.Sender.Uin)
t, err := bot.Client.GetGroupMembers(group)
if err != nil {
log.Warnf("刷新群 %v 成员列表失败: %v", group.Uin, err)
return nil
}
group.Members = t
mem = group.FindMember(m.Sender.Uin)
if mem == nil {
return nil
}
}
ms := gm["sender"].(global.MSG)
switch mem.Permission {
case client.Owner:
ms["role"] = "owner"
case client.Administrator:
ms["role"] = "admin"
case client.Member:
ms["role"] = "member"
default:
ms["role"] = "member"
}
ms["nickname"] = mem.Nickname
ms["card"] = mem.CardName
ms["title"] = mem.SpecialTitle
}
return gm
}
func convertChannelInfo(c *client.ChannelInfo) global.MSG {
slowModes := make([]global.MSG, 0, len(c.Meta.SlowModes))
for _, mode := range c.Meta.SlowModes {

View File

@ -36,11 +36,6 @@ var typeReg = regexp.MustCompile(`\[CQ:(\w+)`)
var paramReg = regexp.MustCompile(`,([\w\-.]+?)=([^,\]]+)`)
*/
const (
maxImageSize = 1024 * 1024 * 30 // 30MB
maxVideoSize = 1024 * 1024 * 100 // 100MB
)
// PokeElement 拍一拍
type PokeElement struct {
Target int64
@ -73,6 +68,27 @@ type LocalVideoElement struct {
thumb io.ReadSeeker
}
// MessageSource 消息来源
// 如果为私聊或者群聊, PrimaryID 将代表群号/QQ号
// 如果为频道, PrimaryID 为 GuildID, SubID 为 ChannelID
type MessageSource struct {
SourceType MessageSourceType
PrimaryID uint64
SubID uint64
}
// MessageSourceType 消息来源类型
type MessageSourceType int32
const (
maxImageSize = 1024 * 1024 * 30 // 30MB
maxVideoSize = 1024 * 1024 * 100 // 100MB
MessageSourcePrivate MessageSourceType = 0
MessageSourceGroup MessageSourceType = 1
MessageSourceGuildChannel MessageSourceType = 2
)
// Type implements the message.IMessageElement.
func (e *LocalImageElement) Type() message.ElementType {
return message.Image
@ -114,16 +130,16 @@ func (e *PokeElement) Type() message.ElementType {
}
// ToArrayMessage 将消息元素数组转为MSG数组以用于消息上报
func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG) {
func ToArrayMessage(e []message.IMessageElement, source MessageSource) (r []global.MSG) {
r = make([]global.MSG, 0, len(e))
m := &message.SendingMessage{Elements: e}
reply := m.FirstOrNil(func(e message.IMessageElement) bool {
_, ok := e.(*message.ReplyElement)
return ok
})
if reply != nil {
if reply != nil && source.SourceType == MessageSourceGroup {
replyElem := reply.(*message.ReplyElement)
rid := groupID
rid := int64(source.PrimaryID)
if rid == 0 {
rid = replyElem.Sender
}
@ -138,7 +154,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG)
"seq": strconv.FormatInt(int64(replyElem.ReplySeq), 10),
"qq": strconv.FormatInt(replyElem.Sender, 10),
"time": strconv.FormatInt(int64(replyElem.Time), 10),
"text": ToStringMessage(replyElem.Elements, groupID),
"text": ToStringMessage(replyElem.Elements, source),
},
})
} else {
@ -260,7 +276,7 @@ func ToArrayMessage(e []message.IMessageElement, groupID int64) (r []global.MSG)
}
// ToStringMessage 将消息元素数组转为字符串以用于消息上报
func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r string) {
func ToStringMessage(e []message.IMessageElement, source MessageSource, isRaw ...bool) (r string) {
sb := global.NewBuffer()
sb.Reset()
write := func(format string, a ...interface{}) {
@ -276,9 +292,9 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
_, ok := e.(*message.ReplyElement)
return ok
})
if reply != nil {
if reply != nil && source.SourceType == MessageSourceGroup {
replyElem := reply.(*message.ReplyElement)
rid := groupID
rid := int64(source.PrimaryID)
if rid == 0 {
rid = replyElem.Sender
}
@ -289,7 +305,7 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
write("[CQ:reply,id=%d,seq=%d,qq=%d,time=%d,text=%s]",
db.ToGlobalID(rid, replyElem.ReplySeq),
replyElem.ReplySeq, replyElem.Sender, replyElem.Time,
CQCodeEscapeValue(ToStringMessage(replyElem.Elements, groupID)))
CQCodeEscapeValue(ToStringMessage(replyElem.Elements, source)))
} else {
write("[CQ:reply,id=%d]", db.ToGlobalID(rid, replyElem.ReplySeq))
}
@ -352,6 +368,8 @@ func ToStringMessage(e []message.IMessageElement, groupID int64, isRaw ...bool)
} else {
write("[CQ:image,file=%s,url=%s%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url), arg)
}
case *message.GuildImageElement:
write("[CQ:image,file=%s,url=%s]", hex.EncodeToString(o.Md5)+".image", CQCodeEscapeValue(o.Url))
case *message.DiceElement:
write("[CQ:dice,value=%v]", o.Value)
case *message.MarketFaceElement:
@ -480,7 +498,7 @@ func ToMessageContent(e []message.IMessageElement) (r []global.MSG) {
}
// ConvertStringMessage 将消息字符串转为消息元素数组
func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IMessageElement) {
func (bot *CQBot) ConvertStringMessage(raw string, sourceType MessageSourceType) (r []message.IMessageElement) {
var t, key string
d := map[string]string{}
@ -516,7 +534,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertStringMessage(customText, isGroup),
Elements: bot.ConvertStringMessage(customText, sourceType),
}
if senderErr != nil {
elem.Sender = sender
@ -532,7 +550,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
ReplySeq: int32(messageSeq),
Sender: sender,
Time: int32(msgTime),
Elements: bot.ConvertStringMessage(customText, isGroup),
Elements: bot.ConvertStringMessage(customText, sourceType),
}
}
r = append([]message.IMessageElement{elem}, r...)
@ -544,7 +562,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertContentMessage(org.GetContent(), isGroup),
Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
},
}, r...)
}
@ -565,7 +583,7 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
}
return
}
elem, err := bot.ToElement(t, d, isGroup)
elem, err := bot.ToElement(t, d, sourceType)
if err != nil {
org := "[CQ:" + t
for k, v := range d {
@ -653,11 +671,11 @@ func (bot *CQBot) ConvertStringMessage(raw string, isGroup bool) (r []message.IM
}
// ConvertObjectMessage 将消息JSON对象转为消息元素数组
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []message.IMessageElement) {
func (bot *CQBot) ConvertObjectMessage(m gjson.Result, sourceType MessageSourceType) (r []message.IMessageElement) {
d := make(map[string]string)
convertElem := func(e gjson.Result) {
t := e.Get("type").Str
if t == "reply" && isGroup {
if t == "reply" && sourceType == MessageSourceGroup {
if len(r) > 0 {
if _, ok := r[0].(*message.ReplyElement); ok {
log.Warnf("警告: 一条信息只能包含一个 Reply 元素.")
@ -688,7 +706,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertStringMessage(customText, isGroup),
Elements: bot.ConvertStringMessage(customText, sourceType),
}
if senderErr != nil {
elem.Sender = sender
@ -704,7 +722,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
ReplySeq: int32(messageSeq),
Sender: sender,
Time: int32(msgTime),
Elements: bot.ConvertStringMessage(customText, isGroup),
Elements: bot.ConvertStringMessage(customText, sourceType),
}
}
r = append([]message.IMessageElement{elem}, r...)
@ -716,7 +734,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
ReplySeq: org.GetAttribute().MessageSeq,
Sender: org.GetAttribute().SenderUin,
Time: int32(org.GetAttribute().Timestamp),
Elements: bot.ConvertContentMessage(org.GetContent(), isGroup),
Elements: bot.ConvertContentMessage(org.GetContent(), sourceType),
},
}, r...)
}
@ -745,7 +763,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
d[key.Str] = value.String()
return true
})
elem, err := bot.ToElement(t, d, isGroup)
elem, err := bot.ToElement(t, d, sourceType)
if err != nil {
log.Warnf("转换CQ码 (%v) 到MiraiGo Element时出现错误: %v 将忽略本段CQ码.", e.Raw, err)
return
@ -758,7 +776,7 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
}
}
if m.Type == gjson.String {
return bot.ConvertStringMessage(m.Str, isGroup)
return bot.ConvertStringMessage(m.Str, sourceType)
}
if m.IsArray() {
m.ForEach(func(_, e gjson.Result) bool {
@ -773,14 +791,14 @@ func (bot *CQBot) ConvertObjectMessage(m gjson.Result, isGroup bool) (r []messag
}
// ConvertContentMessage 将数据库用的 content 转换为消息元素数组
func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []message.IMessageElement) {
func (bot *CQBot) ConvertContentMessage(content []global.MSG, sourceType MessageSourceType) (r []message.IMessageElement) {
for _, c := range content {
data := c["data"].(global.MSG)
switch c["type"] {
case "text":
r = append(r, message.NewText(data["text"].(string)))
case "image":
e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, false, group)
e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, false, sourceType)
if err != nil {
log.Warnf("make image elem error: %v", err)
continue
@ -830,7 +848,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []m
case "face":
r = append(r, message.NewFace(data["id"].(int32)))
case "video":
e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, group)
e, err := bot.makeImageOrVideoElem(map[string]string{"file": data["file"].(string)}, true, sourceType)
if err != nil {
log.Warnf("make image elem error: %v", err)
continue
@ -846,7 +864,7 @@ func (bot *CQBot) ConvertContentMessage(content []global.MSG, group bool) (r []m
// 返回 interface{} 存在三种类型
//
// message.IMessageElement []message.IMessageElement nil
func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m interface{}, err error) {
func (bot *CQBot) ToElement(t string, d map[string]string, sourceType MessageSourceType) (m interface{}, err error) {
switch t {
case "text":
if base.SplitURL {
@ -858,7 +876,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
}
return message.NewText(d["text"]), nil
case "image":
img, err := bot.makeImageOrVideoElem(d, false, isGroup)
img, err := bot.makeImageOrVideoElem(d, false, sourceType)
if err != nil {
return nil, err
}
@ -892,7 +910,7 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
t, _ := strconv.ParseInt(d["qq"], 10, 64)
return &PokeElement{Target: t}, nil
case "gift":
if !isGroup {
if sourceType != MessageSourceGroup {
return nil, errors.New("private gift unsupported") // no free private gift
}
t, _ := strconv.ParseInt(d["qq"], 10, 64)
@ -1088,13 +1106,13 @@ func (bot *CQBot) ToElement(t string, d map[string]string, isGroup bool) (m inte
maxWidth := parseIntWithDefault("maxwidth", 500)
minHeight := parseIntWithDefault("minheight", 200)
maxHeight := parseIntWithDefault("maxheight", 1000)
img, err := bot.makeImageOrVideoElem(d, false, isGroup)
img, err := bot.makeImageOrVideoElem(d, false, sourceType)
if err != nil {
return nil, errors.New("send cardimage faild")
}
return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, isGroup)
return bot.makeShowPic(img, source, brief, icon, minWidth, minHeight, maxWidth, maxHeight, sourceType == MessageSourceGroup)
case "video":
file, err := bot.makeImageOrVideoElem(d, true, isGroup)
file, err := bot.makeImageOrVideoElem(d, true, sourceType)
if err != nil {
return nil, err
}
@ -1248,7 +1266,7 @@ func CQCodeUnescapeValue(content string) string {
}
// makeImageOrVideoElem 图片 elem 生成器,单独拎出来,用于公用
func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (message.IMessageElement, error) {
func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video bool, sourceType MessageSourceType) (message.IMessageElement, error) {
f := d["file"]
if strings.HasPrefix(f, "http") {
hash := md5.Sum([]byte(f))
@ -1327,11 +1345,18 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
b, _ := os.ReadFile(rawPath)
return bot.readVideoCache(b), nil
}
// 目前频道内上传的图片均无法被查询到, 需要单独处理
if sourceType == MessageSourceGuildChannel {
cacheFile := path.Join(global.ImagePath, "guild-images", f)
if global.PathExists(cacheFile) {
return &LocalImageElement{File: cacheFile}, nil
}
}
if strings.HasSuffix(f, ".image") && cache.EnableCacheDB {
hash, err := hex.DecodeString(strings.TrimSuffix(f, ".image"))
if err == nil {
if b := cache.Image.Get(hash); b != nil {
return bot.readImageCache(b, group)
return bot.readImageCache(b, sourceType)
}
}
}
@ -1342,7 +1367,7 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
}
if !exist {
if d["url"] != "" {
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, group)
return bot.makeImageOrVideoElem(map[string]string{"file": d["url"]}, false, sourceType)
}
return nil, errors.New("invalid image")
}
@ -1353,10 +1378,10 @@ func (bot *CQBot) makeImageOrVideoElem(d map[string]string, video, group bool) (
if err != nil {
return nil, err
}
return bot.readImageCache(b, group)
return bot.readImageCache(b, sourceType)
}
func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement, error) {
func (bot *CQBot) readImageCache(b []byte, sourceType MessageSourceType) (message.IMessageElement, error) {
var err error
if len(b) < 20 {
return nil, errors.New("invalid cache")
@ -1368,7 +1393,7 @@ func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement,
imageURL := r.ReadString()
if size == 0 {
if imageURL != "" {
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType)
}
return nil, errors.New("img size is 0")
}
@ -1376,15 +1401,23 @@ func (bot *CQBot) readImageCache(b []byte, group bool) (message.IMessageElement,
return nil, errors.New("invalid hash")
}
var rsp message.IMessageElement
if group {
if sourceType == MessageSourceGroup {
rsp, err = bot.Client.QueryGroupImage(int64(rand.Uint32()), hash, size)
goto ok
}
if sourceType == MessageSourceGuildChannel {
if len(bot.Client.GuildService.Guilds) == 0 {
err = errors.New("cannot query guild image: not any joined guild")
}
guild := bot.Client.GuildService.Guilds[0]
rsp, err = bot.Client.GuildService.QueryImage(guild.GuildId, guild.Channels[0].ChannelId, hash, uint64(size))
goto ok
}
rsp, err = bot.Client.QueryFriendImage(int64(rand.Uint32()), hash, size)
ok:
if err != nil {
if imageURL != "" {
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, group)
return bot.makeImageOrVideoElem(map[string]string{"file": imageURL}, false, sourceType)
}
return nil, err
}

View File

@ -22,18 +22,22 @@ import (
)
// ToFormattedMessage 将给定[]message.IMessageElement转换为通过coolq.SetMessageFormat所定义的消息上报格式
func ToFormattedMessage(e []message.IMessageElement, groupID int64, isRaw ...bool) (r interface{}) {
func ToFormattedMessage(e []message.IMessageElement, source MessageSource, isRaw ...bool) (r interface{}) {
if base.PostFormat == "string" {
r = ToStringMessage(e, groupID, isRaw...)
r = ToStringMessage(e, source, isRaw...)
} else if base.PostFormat == "array" {
r = ToArrayMessage(e, groupID)
r = ToArrayMessage(e, source)
}
return
}
func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMessage) {
bot.checkMedia(m.Elements, m.Sender.Uin)
cqm := ToStringMessage(m.Elements, 0, true)
source := MessageSource{
SourceType: MessageSourcePrivate,
PrimaryID: uint64(m.Sender.Uin),
}
cqm := ToStringMessage(m.Elements, source, true)
id := bot.InsertPrivateMessage(m)
log.Infof("收到好友 %v(%v) 的消息: %v (%v)", m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
fm := global.MSG{
@ -48,7 +52,7 @@ func (bot *CQBot) privateMessageEvent(c *client.QQClient, m *message.PrivateMess
"message_id": id,
"user_id": m.Sender.Uin,
"target_id": m.Target,
"message": ToFormattedMessage(m.Elements, 0, false),
"message": ToFormattedMessage(m.Elements, source, false),
"raw_message": cqm,
"font": 0,
"self_id": c.Uin,
@ -86,7 +90,11 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
return
}
}
cqm := ToStringMessage(m.Elements, m.GroupCode, true)
source := MessageSource{
SourceType: MessageSourceGroup,
PrimaryID: uint64(m.GroupCode),
}
cqm := ToStringMessage(m.Elements, source, true)
id := bot.InsertGroupMessage(m)
log.Infof("收到群 %v(%v) 内 %v(%v) 的消息: %v (%v)", m.GroupName, m.GroupCode, m.Sender.DisplayName(), m.Sender.Uin, cqm, id)
gm := bot.formatGroupMessage(m)
@ -100,7 +108,11 @@ func (bot *CQBot) groupMessageEvent(c *client.QQClient, m *message.GroupMessage)
func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEvent) {
m := e.Message
bot.checkMedia(m.Elements, m.Sender.Uin)
cqm := ToStringMessage(m.Elements, 0, true)
source := MessageSource{
SourceType: MessageSourcePrivate,
PrimaryID: uint64(e.Session.Sender),
}
cqm := ToStringMessage(m.Elements, source, true)
bot.tempSessionCache.Store(m.Sender.Uin, e.Session)
id := m.Id
// todo(Mrs4s)
@ -115,7 +127,7 @@ func (bot *CQBot) tempMessageEvent(c *client.QQClient, e *client.TempMessageEven
"temp_source": e.Session.Source,
"message_id": id,
"user_id": m.Sender.Uin,
"message": ToFormattedMessage(m.Elements, 0, false),
"message": ToFormattedMessage(m.Elements, source, false),
"raw_message": cqm,
"font": 0,
"self_id": c.Uin,
@ -143,7 +155,12 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC
channel = c
}
}
log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, 0, true))
source := MessageSource{
SourceType: MessageSourceGuildChannel,
PrimaryID: m.GuildId,
SubID: m.ChannelId,
}
log.Infof("收到来自频道 %v(%v) 子频道 %v(%v) 内 %v(%v) 的消息: %v", guild.GuildName, guild.GuildId, channel.ChannelName, m.ChannelId, m.Sender.Nickname, m.Sender.TinyId, ToStringMessage(m.Elements, source, true))
// todo: 数据库支持
bot.dispatchEventMessage(global.MSG{
"post_type": "message",
@ -153,7 +170,7 @@ func (bot *CQBot) guildChannelMessageEvent(c *client.QQClient, m *message.GuildC
"channel_id": m.ChannelId,
"message_id": fmt.Sprintf("%v-%v", m.Id, m.InternalId),
"user_id": m.Sender.TinyId,
"message": ToFormattedMessage(m.Elements, 0, false), // todo: 增加对频道消息 Reply 的支持
"message": ToFormattedMessage(m.Elements, source, false), // todo: 增加对频道消息 Reply 的支持
"self_id": bot.Client.Uin,
"self_tiny_id": bot.Client.GuildService.TinyId,
"time": m.Time,
@ -665,6 +682,12 @@ func (bot *CQBot) checkMedia(e []message.IMessageElement, sourceID int64) {
} else if !global.PathExists(path.Join(global.ImagePath, filename)) {
_ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644)
}
if i.Url != "" && !global.PathExists(path.Join(global.ImagePath, "guild-images", filename)) {
if err := global.DownloadFile(i.Url, path.Join(global.ImagePath, "guild-images", filename), -1, map[string]string{}); err != nil {
log.Warnf("下载频道图片时出现错误: %v", err)
}
}
case *message.FriendImageElement:
data := binary.NewWriterF(func(w *binary.Writer) {
w.Write(i.Md5)

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 (
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f
github.com/Microsoft/go-winio v0.5.1
github.com/Mrs4s/MiraiGo v0.0.0-20211111181749-b85fc25cd59b
github.com/Mrs4s/MiraiGo v0.0.0-20211112183036-d3a21e577b02
github.com/dustin/go-humanize v1.0.0
github.com/fumiama/go-hide-param v0.1.4
github.com/gabriel-vasile/mimetype v1.4.0

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

View File

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