mirror of
https://github.com/fumiama/NanoBot.git
synced 2026-06-12 14:10:47 +08:00
feat: add message
This commit is contained in:
8
bot.go
8
bot.go
@@ -357,7 +357,11 @@ func (bot *Bot) Listen() {
|
|||||||
clients.Store(k, bot)
|
clients.Store(k, bot)
|
||||||
continue
|
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
|
bot.seq = payload.S
|
||||||
switch payload.Op {
|
switch payload.Op {
|
||||||
case OpCodeDispatch: // Receive
|
case OpCodeDispatch: // Receive
|
||||||
@@ -390,7 +394,7 @@ func (bot *Bot) Listen() {
|
|||||||
lastheartbeat = time.Now()
|
lastheartbeat = time.Now()
|
||||||
case OpCodeHTTPCallbackACK: // Reply
|
case OpCodeHTTPCallbackACK: // Reply
|
||||||
default:
|
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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
64
context.go
64
context.go
@@ -76,8 +76,53 @@ func (ctx *Ctx) CheckSession() Rule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send 发送消息到对方
|
// Send 发送一批消息
|
||||||
func (ctx *Ctx) Send(replytosender bool, post *MessagePost) (reply *Message, err error) {
|
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
|
msg := ctx.Message
|
||||||
if msg != nil {
|
if msg != nil {
|
||||||
post.ReplyMessageID = msg.ID
|
post.ReplyMessageID = msg.ID
|
||||||
@@ -103,7 +148,7 @@ func (ctx *Ctx) Send(replytosender bool, post *MessagePost) (reply *Message, err
|
|||||||
|
|
||||||
// SendPlainMessage 发送纯文本消息到对方
|
// SendPlainMessage 发送纯文本消息到对方
|
||||||
func (ctx *Ctx) SendPlainMessage(replytosender bool, printable ...any) (*Message, error) {
|
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...)),
|
Content: HideURL(fmt.Sprint(printable...)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -120,7 +165,18 @@ func (ctx *Ctx) SendImage(file string, replytosender bool, caption ...any) (*Mes
|
|||||||
post.ImageFile = file
|
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 向自身分发虚拟事件
|
// Echo 向自身分发虚拟事件
|
||||||
|
|||||||
@@ -15,6 +15,6 @@ func init() {
|
|||||||
if args == "" {
|
if args == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
_, _ = ctx.SendPlainMessage(false, args)
|
_, _ = ctx.SendChain(nano.ReplyTo(ctx.Message.Author.ID), nano.Text(args))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
103
message.go
103
message.go
@@ -1,6 +1,8 @@
|
|||||||
package nano
|
package nano
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -24,3 +26,104 @@ func GetTriggeredMessages(id string) []string {
|
|||||||
defer triggeredMessagesMu.Unlock()
|
defer triggeredMessagesMu.Unlock()
|
||||||
return triggeredMessages.Get(id)
|
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...)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
Ark *MessageArk `json:"ark,omitempty"` // https://bot.q.qq.com/wiki/develop/api/openapi/message/message_template.html
|
||||||
MessageReference *MessageReference `json:"message_reference,omitempty"`
|
MessageReference *MessageReference `json:"message_reference,omitempty"`
|
||||||
Image string `json:"image,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"`
|
ReplyMessageID string `json:"msg_id,omitempty"`
|
||||||
ReplyEventID string `json:"event_id,omitempty"`
|
ReplyEventID string `json:"event_id,omitempty"`
|
||||||
Markdown *MessageMarkdown `json:"markdown,omitempty"`
|
Markdown *MessageMarkdown `json:"markdown,omitempty"`
|
||||||
@@ -237,17 +238,21 @@ func (mp *MessagePost) String() string {
|
|||||||
sb.WriteString(mp.MessageReference.MessageID)
|
sb.WriteString(mp.MessageReference.MessageID)
|
||||||
}
|
}
|
||||||
if mp.Image != "" {
|
if mp.Image != "" {
|
||||||
sb.WriteString(", 图片URL:")
|
sb.WriteString(", 图片URL: ")
|
||||||
sb.WriteString(mp.Image)
|
sb.WriteString(mp.Image)
|
||||||
}
|
}
|
||||||
if mp.ImageFile != "" {
|
if mp.ImageFile != "" {
|
||||||
sb.WriteString(", 图片内容:")
|
sb.WriteString(", 图片内容: ")
|
||||||
x := mp.ImageFile
|
x := mp.ImageFile
|
||||||
if len(x) > 64 {
|
if len(x) > 64 {
|
||||||
x = x[:64] + "..."
|
x = x[:64] + "..."
|
||||||
}
|
}
|
||||||
sb.WriteString(x)
|
sb.WriteString(x)
|
||||||
}
|
}
|
||||||
|
if len(mp.ImageBytes) > 0 {
|
||||||
|
sb.WriteString(", 图片大小: ")
|
||||||
|
sb.WriteString(strconv.Itoa(len(mp.ImageBytes)))
|
||||||
|
}
|
||||||
if mp.Markdown != nil {
|
if mp.Markdown != nil {
|
||||||
sb.WriteString(", MD模版: ")
|
sb.WriteString(", MD模版: ")
|
||||||
sb.WriteString(strconv.Itoa(mp.Markdown.TemplateID))
|
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) {
|
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))
|
return bot.postOpenAPIofMessage(ep, "", WriteBodyFromJSON(content))
|
||||||
}
|
}
|
||||||
x := reflect.ValueOf(content).Elem()
|
x := reflect.ValueOf(content).Elem()
|
||||||
@@ -284,7 +289,7 @@ func (bot *Bot) postMessageTo(ep string, content *MessagePost) (*Message, error)
|
|||||||
msg = append(msg, data)
|
msg = append(msg, data)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
msg = append(msg, xi.String())
|
msg = append(msg, xi.Interface()) // []byte or string
|
||||||
}
|
}
|
||||||
if len(msg) < 2 {
|
if len(msg) < 2 {
|
||||||
return nil, ErrEmptyMessagePost
|
return nil, ErrEmptyMessagePost
|
||||||
|
|||||||
Reference in New Issue
Block a user