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:
8
bot.go
8
bot.go
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
context.go
64
context.go
@@ -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 向自身分发虚拟事件
|
||||
|
||||
@@ -15,6 +15,6 @@ func init() {
|
||||
if args == "" {
|
||||
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
|
||||
|
||||
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...)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user