2026-01-04 21:19:17 +08:00
|
|
|
|
package protocol
|
|
|
|
|
|
|
2026-01-05 05:14:31 +08:00
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
|
)
|
2026-01-04 21:19:17 +08:00
|
|
|
|
|
|
|
|
|
|
// EventType 事件类型
|
|
|
|
|
|
type EventType string
|
|
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
|
// 事件类型常量
|
2026-01-05 05:14:31 +08:00
|
|
|
|
EventTypeMessage EventType = "message"
|
|
|
|
|
|
EventTypeNotice EventType = "notice"
|
|
|
|
|
|
EventTypeRequest EventType = "request"
|
|
|
|
|
|
EventTypeMeta EventType = "meta"
|
|
|
|
|
|
EventTypeMessageSent EventType = "message_sent"
|
|
|
|
|
|
EventTypeNoticeSent EventType = "notice_sent"
|
|
|
|
|
|
EventTypeRequestSent EventType = "request_sent"
|
2026-01-04 21:19:17 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// Event 通用事件接口
|
|
|
|
|
|
// 参考OneBot12协议设计,提供统一的事件抽象
|
|
|
|
|
|
type Event interface {
|
|
|
|
|
|
// GetType 获取事件类型
|
|
|
|
|
|
GetType() EventType
|
|
|
|
|
|
// GetDetailType 获取详细类型
|
|
|
|
|
|
GetDetailType() string
|
|
|
|
|
|
// GetSubType 获取子类型
|
|
|
|
|
|
GetSubType() string
|
|
|
|
|
|
// GetTimestamp 获取时间戳
|
|
|
|
|
|
GetTimestamp() time.Time
|
|
|
|
|
|
// GetSelfID 获取机器人自身ID
|
|
|
|
|
|
GetSelfID() string
|
|
|
|
|
|
// GetData 获取事件数据
|
|
|
|
|
|
GetData() map[string]interface{}
|
2026-01-05 05:14:31 +08:00
|
|
|
|
// Reply 在消息发生的群/私聊进行回复
|
|
|
|
|
|
Reply(ctx context.Context, botManager *BotManager, logger *zap.Logger, message MessageChain) error
|
|
|
|
|
|
// ReplyText 在消息发生的群/私聊进行文本回复(便捷方法)
|
|
|
|
|
|
ReplyText(ctx context.Context, botManager *BotManager, logger *zap.Logger, text string) error
|
2026-01-04 21:19:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// BaseEvent 基础事件结构
|
|
|
|
|
|
type BaseEvent struct {
|
|
|
|
|
|
Type EventType `json:"type"`
|
|
|
|
|
|
DetailType string `json:"detail_type"`
|
|
|
|
|
|
SubType string `json:"sub_type,omitempty"`
|
|
|
|
|
|
Timestamp int64 `json:"timestamp"`
|
|
|
|
|
|
SelfID string `json:"self_id"`
|
|
|
|
|
|
Data map[string]interface{} `json:"data"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetType 获取事件类型
|
|
|
|
|
|
func (e *BaseEvent) GetType() EventType {
|
|
|
|
|
|
return e.Type
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetDetailType 获取详细类型
|
|
|
|
|
|
func (e *BaseEvent) GetDetailType() string {
|
|
|
|
|
|
return e.DetailType
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSubType 获取子类型
|
|
|
|
|
|
func (e *BaseEvent) GetSubType() string {
|
|
|
|
|
|
return e.SubType
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetTimestamp 获取时间戳
|
|
|
|
|
|
func (e *BaseEvent) GetTimestamp() time.Time {
|
|
|
|
|
|
return time.Unix(e.Timestamp, 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetSelfID 获取机器人自身ID
|
|
|
|
|
|
func (e *BaseEvent) GetSelfID() string {
|
|
|
|
|
|
return e.SelfID
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetData 获取事件数据
|
|
|
|
|
|
func (e *BaseEvent) GetData() map[string]interface{} {
|
|
|
|
|
|
return e.Data
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-05 05:14:31 +08:00
|
|
|
|
// Reply 在消息发生的群/私聊进行回复
|
|
|
|
|
|
func (e *BaseEvent) Reply(ctx context.Context, botManager *BotManager, logger *zap.Logger, message MessageChain) error {
|
|
|
|
|
|
data := e.GetData()
|
|
|
|
|
|
userID, _ := data["user_id"]
|
|
|
|
|
|
groupID, _ := data["group_id"]
|
|
|
|
|
|
|
|
|
|
|
|
// 获取 bot 实例
|
|
|
|
|
|
selfID := e.GetSelfID()
|
|
|
|
|
|
bot, ok := botManager.Get(selfID)
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
bots := botManager.GetAll()
|
|
|
|
|
|
if len(bots) == 0 {
|
|
|
|
|
|
logger.Error("No bot instance available")
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
bot = bots[0]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据消息类型发送回复
|
|
|
|
|
|
var action *BaseAction
|
|
|
|
|
|
if e.GetDetailType() == "private" {
|
|
|
|
|
|
action = &BaseAction{
|
|
|
|
|
|
Type: ActionTypeSendPrivateMessage,
|
|
|
|
|
|
Params: map[string]interface{}{
|
|
|
|
|
|
"user_id": userID,
|
|
|
|
|
|
"message": message,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 群聊或其他有 group_id 的事件
|
|
|
|
|
|
action = &BaseAction{
|
|
|
|
|
|
Type: ActionTypeSendGroupMessage,
|
|
|
|
|
|
Params: map[string]interface{}{
|
|
|
|
|
|
"group_id": groupID,
|
|
|
|
|
|
"message": message,
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_, err := bot.SendAction(ctx, action)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
logger.Error("Failed to send reply",
|
|
|
|
|
|
zap.Any("user_id", userID),
|
|
|
|
|
|
zap.Any("group_id", groupID),
|
|
|
|
|
|
zap.String("detail_type", e.GetDetailType()),
|
|
|
|
|
|
zap.Error(err))
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.Debug("Reply sent",
|
|
|
|
|
|
zap.Any("user_id", userID),
|
|
|
|
|
|
zap.Any("group_id", groupID),
|
|
|
|
|
|
zap.String("detail_type", e.GetDetailType()))
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ReplyText 在消息发生的群/私聊进行文本回复(便捷方法)
|
|
|
|
|
|
func (e *BaseEvent) ReplyText(ctx context.Context, botManager *BotManager, logger *zap.Logger, text string) error {
|
|
|
|
|
|
message := NewMessageChain(
|
|
|
|
|
|
NewTextSegment(text),
|
|
|
|
|
|
)
|
|
|
|
|
|
return e.Reply(ctx, botManager, logger, message)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-04 21:19:17 +08:00
|
|
|
|
// MessageEvent 消息事件
|
|
|
|
|
|
type MessageEvent struct {
|
|
|
|
|
|
BaseEvent
|
|
|
|
|
|
MessageID string `json:"message_id"`
|
|
|
|
|
|
Message string `json:"message"`
|
|
|
|
|
|
AltText string `json:"alt_text,omitempty"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// NoticeEvent 通知事件
|
|
|
|
|
|
type NoticeEvent struct {
|
|
|
|
|
|
BaseEvent
|
|
|
|
|
|
GroupID string `json:"group_id,omitempty"`
|
|
|
|
|
|
UserID string `json:"user_id,omitempty"`
|
|
|
|
|
|
Operator string `json:"operator,omitempty"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RequestEvent 请求事件
|
|
|
|
|
|
type RequestEvent struct {
|
|
|
|
|
|
BaseEvent
|
|
|
|
|
|
RequestID string `json:"request_id"`
|
|
|
|
|
|
UserID string `json:"user_id"`
|
|
|
|
|
|
Comment string `json:"comment"`
|
|
|
|
|
|
Flag string `json:"flag"`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MetaEvent 元事件
|
|
|
|
|
|
type MetaEvent struct {
|
|
|
|
|
|
BaseEvent
|
|
|
|
|
|
Status string `json:"status"`
|
|
|
|
|
|
}
|