1
0
mirror of https://github.com/fumiama/NanoBot.git synced 2026-06-06 19:20:24 +08:00

feat: add message

This commit is contained in:
源文雨
2023-10-19 00:41:07 +09:00
parent f674802998
commit 90b0bdb9e4
5 changed files with 180 additions and 12 deletions

8
bot.go
View File

@@ -357,7 +357,11 @@ func (bot *Bot) Listen() {
clients.Store(k, bot)
continue
}
log.Debugln(getLogHeader(), "接收到第", payload.S, "个事件:", payload.Op, ", 类型:", payload.T, ", 数据:", BytesToString(payload.D))
log.Debug(getLogHeader(), " 接收到第 ", payload.S, " 个事件: ", payload.Op, ", 类型: ", payload.T, ", 数据: ", BytesToString(payload.D))
if payload.S <= bot.seq {
log.Warn(getLogHeader(), " 忽略重复编号: ", payload.S, ", 事件: ", payload.Op, ", 类型: ", payload.T)
continue
}
bot.seq = payload.S
switch payload.Op {
case OpCodeDispatch: // Receive
@@ -390,7 +394,7 @@ func (bot *Bot) Listen() {
lastheartbeat = time.Now()
case OpCodeHTTPCallbackACK: // Reply
default:
log.Warnln(getLogHeader(), "忽略未知事件, 序号:", payload.S, ", Op:", payload.Op, ", 类型:", payload.T, ", 数据:", BytesToString(payload.D))
log.Warn(getLogHeader(), " 忽略未知事件, 序号: ", payload.S, ", Op: ", payload.Op, ", 类型: ", payload.T, ", 数据: ", BytesToString(payload.D))
}
}
}

View File

@@ -76,8 +76,53 @@ func (ctx *Ctx) CheckSession() Rule {
}
}
// Send 发送消息到对方
func (ctx *Ctx) Send(replytosender bool, post *MessagePost) (reply *Message, err error) {
// Send 发送一批消息
func (ctx *Ctx) Send(messages Messages) (m []*Message, err error) {
isnextreply := false
textlist := []any{}
var reply *Message
for _, msg := range messages {
switch msg.Type {
case MessageTypeText:
textlist = append(textlist, msg.Data)
case MessageTypeImage:
reply, err = ctx.SendImage(msg.Data, isnextreply, textlist...)
if isnextreply {
isnextreply = false
}
textlist = textlist[:0]
m = append(m, reply)
if err != nil {
return
}
case MessageTypeImageBytes:
reply, err = ctx.SendImageBytes(StringToBytes(msg.Data), isnextreply, textlist...)
if isnextreply {
isnextreply = false
}
textlist = textlist[:0]
m = append(m, reply)
if err != nil {
return
}
case MessageTypeReply:
isnextreply = true
}
}
if len(textlist) > 0 {
reply, err = ctx.SendPlainMessage(isnextreply, textlist...)
m = append(m, reply)
}
return
}
// SendChain 链式发送
func (ctx *Ctx) SendChain(message ...MessageSegment) (m []*Message, err error) {
return ctx.Send(message)
}
// Post 发送消息到对方
func (ctx *Ctx) Post(replytosender bool, post *MessagePost) (reply *Message, err error) {
msg := ctx.Message
if msg != nil {
post.ReplyMessageID = msg.ID
@@ -103,7 +148,7 @@ func (ctx *Ctx) Send(replytosender bool, post *MessagePost) (reply *Message, err
// SendPlainMessage 发送纯文本消息到对方
func (ctx *Ctx) SendPlainMessage(replytosender bool, printable ...any) (*Message, error) {
return ctx.Send(replytosender, &MessagePost{
return ctx.Post(replytosender, &MessagePost{
Content: HideURL(fmt.Sprint(printable...)),
})
}
@@ -120,7 +165,18 @@ func (ctx *Ctx) SendImage(file string, replytosender bool, caption ...any) (*Mes
post.ImageFile = file
}
return ctx.Send(replytosender, post)
return ctx.Post(replytosender, post)
}
// SendImageBytes 发送带图片消息到对方
func (ctx *Ctx) SendImageBytes(data []byte, replytosender bool, caption ...any) (*Message, error) {
post := &MessagePost{
Content: HideURL(fmt.Sprint(caption...)),
}
post.ImageBytes = data
return ctx.Post(replytosender, post)
}
// Echo 向自身分发虚拟事件

View File

@@ -15,6 +15,6 @@ func init() {
if args == "" {
return
}
_, _ = ctx.SendPlainMessage(false, args)
_, _ = ctx.SendChain(nano.ReplyTo(ctx.Message.Author.ID), nano.Text(args))
})
}

View File

@@ -1,6 +1,8 @@
package nano
import (
"fmt"
"strconv"
"sync"
"time"
@@ -24,3 +26,104 @@ func GetTriggeredMessages(id string) []string {
defer triggeredMessagesMu.Unlock()
return triggeredMessages.Get(id)
}
type MessageType int
const (
MessageTypeText MessageType = iota
MessageTypeImage
MessageTypeImageBytes
MessageTypeReply
)
// Message impl the array form of message
type Messages []MessageSegment
// MessageSegment impl the single message
// MessageSegment 消息数组
type MessageSegment struct {
Type MessageType
Data string
}
// String impls the interface fmt.Stringer
func (m MessageSegment) String() string {
return m.Data
}
// Text 纯文本
func Text(text ...interface{}) MessageSegment {
return MessageSegment{
Type: MessageTypeText,
Data: MessageEscape(fmt.Sprint(text...)),
}
}
// Face QQ表情
// https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F
func Face(id int) MessageSegment {
return MessageSegment{
Type: MessageTypeText,
Data: "<emoji:" + strconv.Itoa(id) + ">",
}
}
// Image 普通图片
func Image(file string) MessageSegment {
return MessageSegment{
Type: MessageTypeImage,
Data: file,
}
}
// ImageBytes 普通图片
func ImageBytes(data []byte) MessageSegment {
return MessageSegment{
Type: MessageTypeImageBytes,
Data: BytesToString(data),
}
}
// At @某人
// https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F
func At(id string) MessageSegment {
if id == "all" {
return AtAll()
}
return MessageSegment{
Type: MessageTypeText,
Data: "<@!" + id + ">",
}
}
// AtAll @全体成员
// https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F
func AtAll() MessageSegment {
return MessageSegment{
Type: MessageTypeText,
Data: "@everyone",
}
}
// AtChannel #频道
// https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html#%E6%94%AF%E6%8C%81%E7%9A%84%E6%A0%BC%E5%BC%8F
func AtChannel(id string) MessageSegment {
return MessageSegment{
Type: MessageTypeText,
Data: "<#channel_id>",
}
}
// Reply 回复
// https://github.com/botuniverse/onebot-11/tree/master/message/segment.md#%E5%9B%9E%E5%A4%8D
func ReplyTo(id string) MessageSegment {
return MessageSegment{
Type: MessageTypeReply,
Data: id,
}
}
// ReplyWithMessage returns a reply message
func ReplyWithMessage(messageID string, m ...MessageSegment) Messages {
return append(Messages{ReplyTo(messageID)}, m...)
}

View File

@@ -198,7 +198,8 @@ type MessagePost struct {
Ark *MessageArk `json:"ark,omitempty"` // https://bot.q.qq.com/wiki/develop/api/openapi/message/message_template.html
MessageReference *MessageReference `json:"message_reference,omitempty"`
Image string `json:"image,omitempty"`
ImageFile string `json:"-"` // ImageFile 为图片路径 file:/// or base64:// or base16384:// , 与 Image 参数二选一, 优先 Image
ImageFile string `json:"-"` // ImageFile 为图片路径 file:/// or base64:// or base16384:// , 与 Image, ImageBytes 参数二选一, 优先 ImageBytes
ImageBytes []byte `json:"-"` // ImageBytes 图片数据
ReplyMessageID string `json:"msg_id,omitempty"`
ReplyEventID string `json:"event_id,omitempty"`
Markdown *MessageMarkdown `json:"markdown,omitempty"`
@@ -237,17 +238,21 @@ func (mp *MessagePost) String() string {
sb.WriteString(mp.MessageReference.MessageID)
}
if mp.Image != "" {
sb.WriteString(", 图片URL:")
sb.WriteString(", 图片URL: ")
sb.WriteString(mp.Image)
}
if mp.ImageFile != "" {
sb.WriteString(", 图片内容:")
sb.WriteString(", 图片内容: ")
x := mp.ImageFile
if len(x) > 64 {
x = x[:64] + "..."
}
sb.WriteString(x)
}
if len(mp.ImageBytes) > 0 {
sb.WriteString(", 图片大小: ")
sb.WriteString(strconv.Itoa(len(mp.ImageBytes)))
}
if mp.Markdown != nil {
sb.WriteString(", MD模版: ")
sb.WriteString(strconv.Itoa(mp.Markdown.TemplateID))
@@ -260,7 +265,7 @@ func (mp *MessagePost) String() string {
}
func (bot *Bot) postMessageTo(ep string, content *MessagePost) (*Message, error) {
if content.ImageFile == "" {
if len(content.ImageBytes) == 0 && content.ImageFile == "" {
return bot.postOpenAPIofMessage(ep, "", WriteBodyFromJSON(content))
}
x := reflect.ValueOf(content).Elem()
@@ -284,7 +289,7 @@ func (bot *Bot) postMessageTo(ep string, content *MessagePost) (*Message, error)
msg = append(msg, data)
continue
}
msg = append(msg, xi.String())
msg = append(msg, xi.Interface()) // []byte or string
}
if len(msg) < 2 {
return nil, ErrEmptyMessagePost