diff --git a/coolq/api.go b/coolq/api.go index 2a1b387..17aa396 100644 --- a/coolq/api.go +++ b/coolq/api.go @@ -562,6 +562,47 @@ func (bot *CQBot) CQSendGroupMessage(groupID int64, m gjson.Result, autoEscape b return OK(global.MSG{"message_id": mid}) } +// CQSendGuildChannelMessage 发送频道消息 +// +// @route(send_guild_channel_msg) +// @rename(m->message) +func (bot *CQBot) CQSendGuildChannelMessage(guildID, channelID uint64, m gjson.Result, autoEscape bool) global.MSG { + guild := bot.Client.GuildService.FindGuild(guildID) + if guild == nil { + return Failed(100, "GUILD_NOT_FOUND", "频道不存在") + } + channel := guild.FindChannel(channelID) + if channel == nil { + return Failed(100, "CHANNEL_NOT_FOUND", "子频道不存在") + } + if channel.ChannelType != client.ChannelTypeText { + log.Warnf("无法发送频道信息: 频道类型错误, 不接受文件信息") + return Failed(100, "CHANNEL_NOT_SUPPORTED_TEXT_MSG", "子频道类型错误, 无法发送文本信息") + } + var elem []message.IMessageElement + // todo: 将 converter 适配频道或者为频道单独写一个 + if m.Type == gjson.JSON { + elem = bot.ConvertObjectMessage(m, true) + } else { + str := m.String() + if str == "" { + log.Warn("群消息发送失败: 信息为空.") + return Failed(100, "EMPTY_MSG_ERROR", "消息为空") + } + if autoEscape { + elem = []message.IMessageElement{message.NewText(str)} + } else { + elem = bot.ConvertStringMessage(str, true) + } + } + mid := bot.SendGuildChannelMessage(guildID, channelID, &message.SendingMessage{Elements: elem}) + if mid == "" { + return Failed(100, "SEND_MSG_API_ERROR", "请参考 go-cqhttp 端输出") + } + log.Infof("发送频道 %v(%v) 子频道 %v(%v) 的消息: %v (%v)", guild.GuildName, guild.GuildId, channel.ChannelName, channel.ChannelId, limitedString(m.String()), mid) + return OK(global.MSG{"message_id": mid}) +} + // CQSendGroupForwardMessage 扩展API-发送合并转发(群) // // https://docs.go-cqhttp.org/api/#%E5%8F%91%E9%80%81%E5%90%88%E5%B9%B6%E8%BD%AC%E5%8F%91-%E7%BE%A4 diff --git a/coolq/bot.go b/coolq/bot.go index 2168c3d..f54f8b3 100644 --- a/coolq/bot.go +++ b/coolq/bot.go @@ -181,6 +181,22 @@ func (bot *CQBot) UploadLocalImageAsPrivate(userID int64, img *LocalImageElement return } +// UploadLocalImageAsGuildChannel 上传本地图片至频道 +func (bot *CQBot) UploadLocalImageAsGuildChannel(guildId, channelId uint64, img *LocalImageElement) (*message.GuildImageElement, error) { + if img.File != "" { + f, err := os.Open(img.File) + if err != nil { + return nil, errors.Wrap(err, "open image error") + } + defer func() { _ = f.Close() }() + img.Stream = f + } + if lawful, mime := base.IsLawfulImage(img.Stream); !lawful { + return nil, errors.New("image type error: " + mime) + } + return bot.Client.GuildService.UploadGuildImage(guildId, channelId, img.Stream) +} + // SendGroupMessage 发送群消息 func (bot *CQBot) SendGroupMessage(groupID int64, m *message.SendingMessage) int32 { newElem := make([]message.IMessageElement, 0, len(m.Elements)) @@ -325,6 +341,39 @@ func (bot *CQBot) SendPrivateMessage(target int64, groupID int64, m *message.Sen return id } +// SendGuildChannelMessage 发送频道消息 +func (bot *CQBot) SendGuildChannelMessage(guildID, channelID uint64, m *message.SendingMessage) string { + newElem := make([]message.IMessageElement, 0, len(m.Elements)) + for _, e := range m.Elements { + switch i := e.(type) { + case *LocalImageElement: + n, err := bot.UploadLocalImageAsGuildChannel(guildID, channelID, i) + if err != nil { + log.Warnf("警告: 频道 %d 消息%s上传失败: %v", channelID, e.Type().String(), err) + continue + } + e = n + case *LocalVideoElement, *LocalVoiceElement, *PokeElement, *message.MusicShareElement, *GiftElement: + log.Warnf("警告: 频道暂不支持发送 %v 消息", i.Type().String()) + continue + } + newElem = append(newElem, e) + } + if len(newElem) == 0 { + log.Warnf("频道消息发送失败: 消息为空.") + return "" + } + m.Elements = newElem + bot.checkMedia(newElem, bot.Client.Uin) + ret, err := bot.Client.GuildService.SendGuildChannelMessage(guildID, channelID, m) + if err != nil { + log.Warnf("频道消息发送失败: %v", err) + return "" + } + // todo: insert db + return fmt.Sprintf("%v-%v", ret.Id, ret.InternalId) +} + // InsertGroupMessage 群聊消息入数据库 func (bot *CQBot) InsertGroupMessage(m *message.GroupMessage) int32 { t := &message.SendingMessage{Elements: m.Elements} diff --git a/coolq/event.go b/coolq/event.go index 5212520..a9f4829 100644 --- a/coolq/event.go +++ b/coolq/event.go @@ -652,6 +652,19 @@ 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) } + case *message.GuildImageElement: + data := binary.NewWriterF(func(w *binary.Writer) { + w.Write(i.Md5) + w.WriteUInt32(uint32(i.Size)) + w.WriteString(i.DownloadIndex) + w.WriteString(i.Url) + }) + filename := hex.EncodeToString(i.Md5) + ".image" + if cache.EnableCacheDB { + cache.Image.Insert(i.Md5, data) + } else if !global.PathExists(path.Join(global.ImagePath, filename)) { + _ = os.WriteFile(path.Join(global.ImagePath, filename), data, 0o644) + } case *message.FriendImageElement: data := binary.NewWriterF(func(w *binary.Writer) { w.Write(i.Md5) diff --git a/modules/api/api.go b/modules/api/api.go index fba3c06..af21228 100644 --- a/modules/api/api.go +++ b/modules/api/api.go @@ -177,6 +177,12 @@ func (c *Caller) call(action string, p Getter) global.MSG { p1 := p.Get("message") p2 := p.Get("auto_escape").Bool() return c.bot.CQSendGroupMessage(p0, p1, p2) + case "send_guild_channel_msg": + p0 := p.Get("guild_id").Uint() + p1 := p.Get("channel_id").Uint() + p2 := p.Get("message") + p3 := p.Get("auto_escape").Bool() + return c.bot.CQSendGuildChannelMessage(p0, p1, p2, p3) case "send_msg": p0 := p.Get("group_id").Int() p1 := p.Get("user_id").Int()