refactor: modernize codebase for Go 1.26 and fix adapter issues
- Upgrade Go version from 1.24 to 1.26.1 with updated dependencies
- Replace interface{} with any type throughout codebase
- Add message deduplication in OneBot11 adapter to prevent duplicate event processing
- URL-encode access token in WebSocket connections to handle special characters
- Remove duplicate bot startup hooks in DI providers (now handled by botManager.StartAll)
- Use slices.Clone and errgroup.Go patterns for cleaner concurrent code
- Apply omitzero JSON tags for optional fields (Go 1.24+ feature)
This commit is contained in:
@@ -225,7 +225,7 @@ func (a *Adapter) handleEvents(eventChan <-chan []byte) {
|
||||
}
|
||||
|
||||
// SendAction 发送动作
|
||||
func (a *Adapter) SendAction(ctx context.Context, action protocol.Action) (map[string]interface{}, error) {
|
||||
func (a *Adapter) SendAction(ctx context.Context, action protocol.Action) (map[string]any, error) {
|
||||
// 调用 API
|
||||
resp, err := a.apiClient.Call(ctx, string(action.GetType()), action.GetParams())
|
||||
if err != nil {
|
||||
@@ -303,8 +303,8 @@ func (a *Adapter) IsConnected() bool {
|
||||
}
|
||||
|
||||
// GetStats 获取统计信息
|
||||
func (a *Adapter) GetStats() map[string]interface{} {
|
||||
stats := map[string]interface{}{
|
||||
func (a *Adapter) GetStats() map[string]any {
|
||||
stats := map[string]any{
|
||||
"protocol": "milky",
|
||||
"self_id": a.selfID,
|
||||
"event_mode": a.config.EventMode,
|
||||
@@ -320,7 +320,7 @@ func (a *Adapter) GetStats() map[string]interface{} {
|
||||
}
|
||||
|
||||
// CallAPI 直接调用 API(提供给 Bot 使用)
|
||||
func (a *Adapter) CallAPI(ctx context.Context, endpoint string, params map[string]interface{}) (*APIResponse, error) {
|
||||
func (a *Adapter) CallAPI(ctx context.Context, endpoint string, params map[string]any) (*APIResponse, error) {
|
||||
return a.apiClient.Call(ctx, endpoint, params)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func NewAPIClient(baseURL, accessToken string, timeout time.Duration, retryCount
|
||||
// endpoint: API 端点名称(如 "send_private_message")
|
||||
// input: 输入参数(会被序列化为 JSON)
|
||||
// 返回: 响应数据和错误
|
||||
func (c *APIClient) Call(ctx context.Context, endpoint string, input interface{}) (*APIResponse, error) {
|
||||
func (c *APIClient) Call(ctx context.Context, endpoint string, input any) (*APIResponse, error) {
|
||||
// 序列化输入参数
|
||||
var inputData []byte
|
||||
var err error
|
||||
@@ -126,7 +126,7 @@ func (c *APIClient) doRequest(ctx context.Context, url string, inputData []byte)
|
||||
// CallWithoutRetry 调用 API(不重试)
|
||||
// 注意:pkg/net.HTTPClient 的通用请求已经内置了重试机制
|
||||
// 这个方法保留是为了向后兼容,但仍然会使用底层的重试机制
|
||||
func (c *APIClient) CallWithoutRetry(ctx context.Context, endpoint string, input interface{}) (*APIResponse, error) {
|
||||
func (c *APIClient) CallWithoutRetry(ctx context.Context, endpoint string, input any) (*APIResponse, error) {
|
||||
return c.Call(ctx, endpoint, input)
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ func (b *Bot) Disconnect(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// SendAction 发送动作
|
||||
func (b *Bot) SendAction(ctx context.Context, action protocol.Action) (map[string]interface{}, error) {
|
||||
func (b *Bot) SendAction(ctx context.Context, action protocol.Action) (map[string]any, error) {
|
||||
if b.status != protocol.BotStatusRunning {
|
||||
return nil, fmt.Errorf("bot is not running")
|
||||
}
|
||||
@@ -119,8 +119,8 @@ func (b *Bot) GetAdapter() *Adapter {
|
||||
}
|
||||
|
||||
// GetInfo 获取机器人信息
|
||||
func (b *Bot) GetInfo() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func (b *Bot) GetInfo() map[string]any {
|
||||
return map[string]any{
|
||||
"id": b.id,
|
||||
"protocol": "milky",
|
||||
"status": b.status,
|
||||
@@ -144,7 +144,7 @@ func (b *Bot) SetStatus(status protocol.BotStatus) {
|
||||
|
||||
// SendPrivateMessage 发送私聊消息
|
||||
func (b *Bot) SendPrivateMessage(ctx context.Context, userID int64, segments []OutgoingSegment) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"user_id": userID,
|
||||
"segments": segments,
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func (b *Bot) SendPrivateMessage(ctx context.Context, userID int64, segments []O
|
||||
|
||||
// SendGroupMessage 发送群消息
|
||||
func (b *Bot) SendGroupMessage(ctx context.Context, groupID int64, segments []OutgoingSegment) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"segments": segments,
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func (b *Bot) SendGroupMessage(ctx context.Context, groupID int64, segments []Ou
|
||||
|
||||
// SendTempMessage 发送临时消息
|
||||
func (b *Bot) SendTempMessage(ctx context.Context, groupID, userID int64, segments []OutgoingSegment) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"segments": segments,
|
||||
@@ -172,7 +172,7 @@ func (b *Bot) SendTempMessage(ctx context.Context, groupID, userID int64, segmen
|
||||
|
||||
// RecallMessage 撤回消息
|
||||
func (b *Bot) RecallMessage(ctx context.Context, messageScene string, peerID, messageSeq int64) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"message_scene": messageScene,
|
||||
"peer_id": peerID,
|
||||
"message_seq": messageSeq,
|
||||
@@ -192,7 +192,7 @@ func (b *Bot) GetGroupList(ctx context.Context) (*APIResponse, error) {
|
||||
|
||||
// GetGroupMemberList 获取群成员列表
|
||||
func (b *Bot) GetGroupMemberList(ctx context.Context, groupID int64) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
}
|
||||
return b.adapter.CallAPI(ctx, "get_group_member_list", params)
|
||||
@@ -200,7 +200,7 @@ func (b *Bot) GetGroupMemberList(ctx context.Context, groupID int64) (*APIRespon
|
||||
|
||||
// GetGroupMemberInfo 获取群成员信息
|
||||
func (b *Bot) GetGroupMemberInfo(ctx context.Context, groupID, userID int64) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func (b *Bot) GetGroupMemberInfo(ctx context.Context, groupID, userID int64) (*A
|
||||
|
||||
// SetGroupAdmin 设置群管理员
|
||||
func (b *Bot) SetGroupAdmin(ctx context.Context, groupID, userID int64, isSet bool) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"is_set": isSet,
|
||||
@@ -219,7 +219,7 @@ func (b *Bot) SetGroupAdmin(ctx context.Context, groupID, userID int64, isSet bo
|
||||
|
||||
// SetGroupCard 设置群名片
|
||||
func (b *Bot) SetGroupCard(ctx context.Context, groupID, userID int64, card string) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"card": card,
|
||||
@@ -229,7 +229,7 @@ func (b *Bot) SetGroupCard(ctx context.Context, groupID, userID int64, card stri
|
||||
|
||||
// SetGroupName 设置群名
|
||||
func (b *Bot) SetGroupName(ctx context.Context, groupID int64, groupName string) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"group_name": groupName,
|
||||
}
|
||||
@@ -238,7 +238,7 @@ func (b *Bot) SetGroupName(ctx context.Context, groupID int64, groupName string)
|
||||
|
||||
// KickGroupMember 踢出群成员
|
||||
func (b *Bot) KickGroupMember(ctx context.Context, groupID, userID int64, rejectAddRequest bool) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"reject_add_request": rejectAddRequest,
|
||||
@@ -248,7 +248,7 @@ func (b *Bot) KickGroupMember(ctx context.Context, groupID, userID int64, reject
|
||||
|
||||
// MuteGroupMember 禁言群成员
|
||||
func (b *Bot) MuteGroupMember(ctx context.Context, groupID, userID int64, duration int32) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"duration": duration,
|
||||
@@ -258,7 +258,7 @@ func (b *Bot) MuteGroupMember(ctx context.Context, groupID, userID int64, durati
|
||||
|
||||
// MuteGroupWhole 全体禁言
|
||||
func (b *Bot) MuteGroupWhole(ctx context.Context, groupID int64, isMute bool) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"is_mute": isMute,
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func (b *Bot) MuteGroupWhole(ctx context.Context, groupID int64, isMute bool) (*
|
||||
|
||||
// LeaveGroup 退出群
|
||||
func (b *Bot) LeaveGroup(ctx context.Context, groupID int64) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
}
|
||||
return b.adapter.CallAPI(ctx, "leave_group", params)
|
||||
@@ -275,7 +275,7 @@ func (b *Bot) LeaveGroup(ctx context.Context, groupID int64) (*APIResponse, erro
|
||||
|
||||
// HandleFriendRequest 处理好友请求
|
||||
func (b *Bot) HandleFriendRequest(ctx context.Context, initiatorUID string, accept bool) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"initiator_uid": initiatorUID,
|
||||
"accept": accept,
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func (b *Bot) HandleFriendRequest(ctx context.Context, initiatorUID string, acce
|
||||
|
||||
// HandleGroupJoinRequest 处理入群申请
|
||||
func (b *Bot) HandleGroupJoinRequest(ctx context.Context, groupID, notificationSeq int64, accept bool, rejectReason string) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"notification_seq": notificationSeq,
|
||||
"accept": accept,
|
||||
@@ -295,7 +295,7 @@ func (b *Bot) HandleGroupJoinRequest(ctx context.Context, groupID, notificationS
|
||||
|
||||
// HandleGroupInvitation 处理群邀请
|
||||
func (b *Bot) HandleGroupInvitation(ctx context.Context, groupID, invitationSeq int64, accept bool) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"group_id": groupID,
|
||||
"invitation_seq": invitationSeq,
|
||||
"accept": accept,
|
||||
@@ -305,7 +305,7 @@ func (b *Bot) HandleGroupInvitation(ctx context.Context, groupID, invitationSeq
|
||||
|
||||
// UploadFile 上传文件
|
||||
func (b *Bot) UploadFile(ctx context.Context, fileType, filePath string) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"file_type": fileType,
|
||||
"file_path": filePath,
|
||||
}
|
||||
@@ -314,7 +314,7 @@ func (b *Bot) UploadFile(ctx context.Context, fileType, filePath string) (*APIRe
|
||||
|
||||
// GetFile 获取文件
|
||||
func (b *Bot) GetFile(ctx context.Context, fileID string) (*APIResponse, error) {
|
||||
params := map[string]interface{}{
|
||||
params := map[string]any{
|
||||
"file_id": fileID,
|
||||
}
|
||||
return b.adapter.CallAPI(ctx, "get_file", params)
|
||||
|
||||
@@ -101,7 +101,7 @@ func (c *EventConverter) convertMessageEvent(milkyEvent *Event) (protocol.Event,
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"peer_id": msgData.PeerID,
|
||||
"message_seq": msgData.MessageSeq,
|
||||
"sender_id": msgData.SenderID,
|
||||
@@ -145,7 +145,7 @@ func (c *EventConverter) convertFriendRequestEvent(milkyEvent *Event) (protocol.
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"initiator_id": data.InitiatorID,
|
||||
"initiator_uid": data.InitiatorUID,
|
||||
"comment": data.Comment,
|
||||
@@ -178,7 +178,7 @@ func (c *EventConverter) convertGroupJoinRequestEvent(milkyEvent *Event) (protoc
|
||||
SubType: "add",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"group_id": data.GroupID,
|
||||
"notification_seq": data.NotificationSeq,
|
||||
"is_filtered": data.IsFiltered,
|
||||
@@ -212,7 +212,7 @@ func (c *EventConverter) convertGroupInvitedJoinRequestEvent(milkyEvent *Event)
|
||||
SubType: "invite",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"group_id": data.GroupID,
|
||||
"notification_seq": data.NotificationSeq,
|
||||
"initiator_id": data.InitiatorID,
|
||||
@@ -245,7 +245,7 @@ func (c *EventConverter) convertGroupInvitationEvent(milkyEvent *Event) (protoco
|
||||
SubType: "invite_self",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"group_id": data.GroupID,
|
||||
"invitation_seq": data.InvitationSeq,
|
||||
"initiator_id": data.InitiatorID,
|
||||
@@ -277,7 +277,7 @@ func (c *EventConverter) convertMessageRecallEvent(milkyEvent *Event) (protocol.
|
||||
SubType: data.MessageScene,
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"message_scene": data.MessageScene,
|
||||
"peer_id": data.PeerID,
|
||||
"message_seq": data.MessageSeq,
|
||||
@@ -310,7 +310,7 @@ func (c *EventConverter) convertBotOfflineEvent(milkyEvent *Event) (protocol.Eve
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"reason": data.Reason,
|
||||
},
|
||||
},
|
||||
@@ -337,7 +337,7 @@ func (c *EventConverter) convertFriendNudgeEvent(milkyEvent *Event) (protocol.Ev
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
}
|
||||
@@ -362,7 +362,7 @@ func (c *EventConverter) convertFriendFileUploadEvent(milkyEvent *Event) (protoc
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
}
|
||||
@@ -392,7 +392,7 @@ func (c *EventConverter) convertGroupAdminChangeEvent(milkyEvent *Event) (protoc
|
||||
SubType: subType,
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
@@ -423,7 +423,7 @@ func (c *EventConverter) convertGroupEssenceMessageChangeEvent(milkyEvent *Event
|
||||
SubType: subType,
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
}
|
||||
@@ -448,7 +448,7 @@ func (c *EventConverter) convertGroupMemberIncreaseEvent(milkyEvent *Event) (pro
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
@@ -478,7 +478,7 @@ func (c *EventConverter) convertGroupMemberDecreaseEvent(milkyEvent *Event) (pro
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
@@ -508,7 +508,7 @@ func (c *EventConverter) convertGroupNameChangeEvent(milkyEvent *Event) (protoco
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
Operator: strconv.FormatInt(data.OperatorID, 10),
|
||||
@@ -539,7 +539,7 @@ func (c *EventConverter) convertGroupMessageReactionEvent(milkyEvent *Event) (pr
|
||||
SubType: subType,
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
@@ -570,7 +570,7 @@ func (c *EventConverter) convertGroupMuteEvent(milkyEvent *Event) (protocol.Even
|
||||
SubType: subType,
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
@@ -602,7 +602,7 @@ func (c *EventConverter) convertGroupWholeMuteEvent(milkyEvent *Event) (protocol
|
||||
SubType: subType,
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
Operator: strconv.FormatInt(data.OperatorID, 10),
|
||||
@@ -628,7 +628,7 @@ func (c *EventConverter) convertGroupNudgeEvent(milkyEvent *Event) (protocol.Eve
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.SenderID, 10),
|
||||
@@ -654,7 +654,7 @@ func (c *EventConverter) convertGroupFileUploadEvent(milkyEvent *Event) (protoco
|
||||
SubType: "",
|
||||
Timestamp: milkyEvent.Time,
|
||||
SelfID: selfID,
|
||||
Data: map[string]interface{}(milkyEvent.Data),
|
||||
Data: map[string]any(milkyEvent.Data),
|
||||
},
|
||||
GroupID: strconv.FormatInt(data.GroupID, 10),
|
||||
UserID: strconv.FormatInt(data.UserID, 10),
|
||||
|
||||
@@ -25,14 +25,14 @@ type Boolean = bool
|
||||
|
||||
// IncomingSegment 接收消息段(联合类型)
|
||||
type IncomingSegment struct {
|
||||
Type string `json:"type"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
Type string `json:"type"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
|
||||
// OutgoingSegment 发送消息段(联合类型)
|
||||
type OutgoingSegment struct {
|
||||
Type string `json:"type"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
Type string `json:"type"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
|
||||
// IncomingForwardedMessage 接收转发消息
|
||||
@@ -318,10 +318,10 @@ type GroupFileUploadEventData struct {
|
||||
|
||||
// Event Milky 事件(联合类型,使用 event_type 区分)
|
||||
type Event struct {
|
||||
EventType string `json:"event_type"`
|
||||
Time int64 `json:"time"`
|
||||
SelfID int64 `json:"self_id"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
EventType string `json:"event_type"`
|
||||
Time int64 `json:"time"`
|
||||
SelfID int64 `json:"self_id"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
|
||||
// 事件类型常量
|
||||
@@ -353,10 +353,10 @@ const (
|
||||
|
||||
// APIResponse API 响应
|
||||
type APIResponse struct {
|
||||
Status string `json:"status"` // ok, failed
|
||||
RetCode int `json:"retcode"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Status string `json:"status"` // ok, failed
|
||||
RetCode int `json:"retcode"`
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// 响应状态码
|
||||
|
||||
@@ -82,16 +82,16 @@ func ConvertAction(action protocol.Action) string {
|
||||
|
||||
// SendPrivateMessageAction 发送私聊消息动作
|
||||
type SendPrivateMessageAction struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Message interface{} `json:"message"`
|
||||
AutoEscape bool `json:"auto_escape,omitempty"`
|
||||
UserID int64 `json:"user_id"`
|
||||
Message any `json:"message"`
|
||||
AutoEscape bool `json:"auto_escape,omitempty"`
|
||||
}
|
||||
|
||||
// SendGroupMessageAction 发送群消息动作
|
||||
type SendGroupMessageAction struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
Message interface{} `json:"message"`
|
||||
AutoEscape bool `json:"auto_escape,omitempty"`
|
||||
GroupID int64 `json:"group_id"`
|
||||
Message any `json:"message"`
|
||||
AutoEscape bool `json:"auto_escape,omitempty"`
|
||||
}
|
||||
|
||||
// DeleteMessageAction 撤回消息动作
|
||||
@@ -224,12 +224,12 @@ type SetRestartAction struct {
|
||||
|
||||
// ActionResponse API响应
|
||||
type ActionResponse struct {
|
||||
Status string `json:"status"`
|
||||
RetCode int `json:"retcode"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Echo string `json:"echo,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Wording string `json:"wording,omitempty"`
|
||||
Status string `json:"status"`
|
||||
RetCode int `json:"retcode"`
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
Echo string `json:"echo,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Wording string `json:"wording,omitempty"`
|
||||
}
|
||||
|
||||
// 响应状态码常量
|
||||
@@ -245,7 +245,7 @@ const (
|
||||
)
|
||||
|
||||
// BuildActionRequest 构建动作请求
|
||||
func BuildActionRequest(action string, params map[string]interface{}, echo string) *OB11Action {
|
||||
func BuildActionRequest(action string, params map[string]any, echo string) *OB11Action {
|
||||
return &OB11Action{
|
||||
Action: action,
|
||||
Params: params,
|
||||
@@ -254,8 +254,8 @@ func BuildActionRequest(action string, params map[string]interface{}, echo strin
|
||||
}
|
||||
|
||||
// BuildSendPrivateMsg 构建发送私聊消息请求
|
||||
func BuildSendPrivateMsg(userID int64, message interface{}, autoEscape bool) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func BuildSendPrivateMsg(userID int64, message any, autoEscape bool) map[string]any {
|
||||
return map[string]any{
|
||||
"user_id": userID,
|
||||
"message": message,
|
||||
"auto_escape": autoEscape,
|
||||
@@ -263,8 +263,8 @@ func BuildSendPrivateMsg(userID int64, message interface{}, autoEscape bool) map
|
||||
}
|
||||
|
||||
// BuildSendGroupMsg 构建发送群消息请求
|
||||
func BuildSendGroupMsg(groupID int64, message interface{}, autoEscape bool) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func BuildSendGroupMsg(groupID int64, message any, autoEscape bool) map[string]any {
|
||||
return map[string]any{
|
||||
"group_id": groupID,
|
||||
"message": message,
|
||||
"auto_escape": autoEscape,
|
||||
@@ -272,15 +272,15 @@ func BuildSendGroupMsg(groupID int64, message interface{}, autoEscape bool) map[
|
||||
}
|
||||
|
||||
// BuildDeleteMsg 构建撤回消息请求
|
||||
func BuildDeleteMsg(messageID int32) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func BuildDeleteMsg(messageID int32) map[string]any {
|
||||
return map[string]any{
|
||||
"message_id": messageID,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildSetGroupBan 构建群组禁言请求
|
||||
func BuildSetGroupBan(groupID, userID int64, duration int64) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func BuildSetGroupBan(groupID, userID int64, duration int64) map[string]any {
|
||||
return map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"duration": duration,
|
||||
@@ -288,8 +288,8 @@ func BuildSetGroupBan(groupID, userID int64, duration int64) map[string]interfac
|
||||
}
|
||||
|
||||
// BuildSetGroupKick 构建群组踢人请求
|
||||
func BuildSetGroupKick(groupID, userID int64, rejectAddRequest bool) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func BuildSetGroupKick(groupID, userID int64, rejectAddRequest bool) map[string]any {
|
||||
return map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"reject_add_request": rejectAddRequest,
|
||||
@@ -297,8 +297,8 @@ func BuildSetGroupKick(groupID, userID int64, rejectAddRequest bool) map[string]
|
||||
}
|
||||
|
||||
// BuildSetGroupCard 构建设置群名片请求
|
||||
func BuildSetGroupCard(groupID, userID int64, card string) map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
func BuildSetGroupCard(groupID, userID int64, card string) map[string]any {
|
||||
return map[string]any{
|
||||
"group_id": groupID,
|
||||
"user_id": userID,
|
||||
"card": card,
|
||||
|
||||
@@ -3,6 +3,7 @@ package onebot11
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -28,6 +29,10 @@ type Adapter struct {
|
||||
wsConnection *net.WebSocketConnection
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
|
||||
// 消息去重
|
||||
processedMessages map[int64]time.Time
|
||||
messageMu sync.RWMutex
|
||||
}
|
||||
|
||||
// Config OneBot11配置
|
||||
@@ -65,16 +70,20 @@ func NewAdapter(config *Config, logger *zap.Logger, wsManager *net.WebSocketMana
|
||||
}
|
||||
|
||||
adapter := &Adapter{
|
||||
config: config,
|
||||
logger: logger.Named("onebot11"),
|
||||
wsManager: wsManager,
|
||||
wsWaiter: NewWSResponseWaiter(timeout, logger),
|
||||
eventBus: eventBus,
|
||||
selfID: config.SelfID,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
config: config,
|
||||
logger: logger.Named("onebot11"),
|
||||
wsManager: wsManager,
|
||||
wsWaiter: NewWSResponseWaiter(timeout, logger),
|
||||
eventBus: eventBus,
|
||||
selfID: config.SelfID,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
processedMessages: make(map[int64]time.Time),
|
||||
}
|
||||
|
||||
// 启动定期清理过期消息的 goroutine
|
||||
go adapter.cleanupProcessedMessages()
|
||||
|
||||
// 如果使用HTTP连接,初始化HTTP客户端
|
||||
if config.ConnectionType == "http" && config.HTTPUrl != "" {
|
||||
adapter.httpClient = NewHTTPClient(config.HTTPUrl, config.AccessToken, timeout, logger)
|
||||
@@ -83,6 +92,43 @@ func NewAdapter(config *Config, logger *zap.Logger, wsManager *net.WebSocketMana
|
||||
return adapter
|
||||
}
|
||||
|
||||
// cleanupProcessedMessages 定期清理过期的已处理消息记录
|
||||
func (a *Adapter) cleanupProcessedMessages() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-a.ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.messageMu.Lock()
|
||||
now := time.Now()
|
||||
for msgID, processedAt := range a.processedMessages {
|
||||
if now.Sub(processedAt) > 10*time.Minute {
|
||||
delete(a.processedMessages, msgID)
|
||||
}
|
||||
}
|
||||
a.messageMu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isMessageProcessed 检查消息是否已处理
|
||||
func (a *Adapter) isMessageProcessed(messageID int64) bool {
|
||||
a.messageMu.RLock()
|
||||
_, exists := a.processedMessages[messageID]
|
||||
a.messageMu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// markMessageProcessed 标记消息为已处理
|
||||
func (a *Adapter) markMessageProcessed(messageID int64) {
|
||||
a.messageMu.Lock()
|
||||
a.processedMessages[messageID] = time.Now()
|
||||
a.messageMu.Unlock()
|
||||
}
|
||||
|
||||
// Name 获取协议名称
|
||||
func (a *Adapter) Name() string {
|
||||
return "OneBot"
|
||||
@@ -174,7 +220,7 @@ func (a *Adapter) GetSelfID() string {
|
||||
}
|
||||
|
||||
// SendAction 发送动作
|
||||
func (a *Adapter) SendAction(ctx context.Context, action protocol.Action) (map[string]interface{}, error) {
|
||||
func (a *Adapter) SendAction(ctx context.Context, action protocol.Action) (map[string]any, error) {
|
||||
// 序列化为OneBot11格式
|
||||
data, err := a.SerializeAction(action)
|
||||
if err != nil {
|
||||
@@ -228,7 +274,7 @@ func (a *Adapter) SerializeAction(action protocol.Action) ([]byte, error) {
|
||||
}
|
||||
|
||||
// 复制参数并转换消息链
|
||||
params := make(map[string]interface{})
|
||||
params := make(map[string]any)
|
||||
for k, v := range action.GetParams() {
|
||||
// 检查是否是消息链
|
||||
if k == "message" {
|
||||
@@ -259,10 +305,10 @@ func (a *Adapter) connectWebSocket(ctx context.Context) error {
|
||||
zap.String("url", a.config.WSUrl),
|
||||
zap.Bool("has_token", a.config.AccessToken != ""))
|
||||
|
||||
// 添加访问令牌到URL
|
||||
// 添加访问令牌到URL(需要对token进行URL编码以处理特殊字符)
|
||||
wsURL := a.config.WSUrl
|
||||
if a.config.AccessToken != "" {
|
||||
wsURL += "?access_token=" + a.config.AccessToken
|
||||
wsURL += "?access_token=" + url.QueryEscape(a.config.AccessToken)
|
||||
a.logger.Debug("Added access token to WebSocket URL")
|
||||
}
|
||||
|
||||
@@ -342,7 +388,7 @@ func (a *Adapter) connectHTTPPost(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// sendActionWebSocket 通过WebSocket发送动作
|
||||
func (a *Adapter) sendActionWebSocket(data []byte) (map[string]interface{}, error) {
|
||||
func (a *Adapter) sendActionWebSocket(data []byte) (map[string]any, error) {
|
||||
if a.wsConnection == nil {
|
||||
return nil, fmt.Errorf("websocket connection not established")
|
||||
}
|
||||
@@ -383,7 +429,7 @@ func (a *Adapter) sendActionWebSocket(data []byte) (map[string]interface{}, erro
|
||||
}
|
||||
|
||||
// sendActionHTTP 通过HTTP发送动作
|
||||
func (a *Adapter) sendActionHTTP(data []byte) (map[string]interface{}, error) {
|
||||
func (a *Adapter) sendActionHTTP(data []byte) (map[string]any, error) {
|
||||
if a.httpClient == nil {
|
||||
return nil, fmt.Errorf("http client not initialized")
|
||||
}
|
||||
@@ -439,7 +485,7 @@ func (a *Adapter) handleWebSocketMessages() {
|
||||
zap.String("preview", string(message[:min(len(message), 200)])))
|
||||
|
||||
// 尝试解析为响应(先检查是否有 echo 字段)
|
||||
var tempMap map[string]interface{}
|
||||
var tempMap map[string]any
|
||||
if err := sonic.Unmarshal(message, &tempMap); err == nil {
|
||||
if echo, ok := tempMap["echo"].(string); ok && echo != "" {
|
||||
// 有 echo 字段,说明是API响应
|
||||
@@ -478,6 +524,24 @@ func (a *Adapter) handleWebSocketMessages() {
|
||||
continue
|
||||
}
|
||||
|
||||
// 消息去重检查(基于 message_id)
|
||||
data := event.GetData()
|
||||
// message_id 可能是 int32 或 int64
|
||||
var messageID int64
|
||||
if mid, ok := data["message_id"].(int32); ok && mid > 0 {
|
||||
messageID = int64(mid)
|
||||
} else if mid, ok := data["message_id"].(int64); ok && mid > 0 {
|
||||
messageID = mid
|
||||
}
|
||||
if messageID > 0 {
|
||||
if a.isMessageProcessed(messageID) {
|
||||
a.logger.Warn("Duplicate message detected, skipping",
|
||||
zap.Int64("message_id", messageID))
|
||||
continue
|
||||
}
|
||||
a.markMessageProcessed(messageID)
|
||||
}
|
||||
|
||||
// 发布事件到事件总线
|
||||
a.logger.Info("Publishing event to event bus",
|
||||
zap.String("event_type", string(event.GetType())),
|
||||
|
||||
@@ -41,9 +41,9 @@ func NewHTTPClient(baseURL, accessToken string, timeout time.Duration, logger *z
|
||||
}
|
||||
|
||||
// Call 调用API
|
||||
func (c *HTTPClient) Call(ctx context.Context, action string, params map[string]interface{}) (*OB11Response, error) {
|
||||
func (c *HTTPClient) Call(ctx context.Context, action string, params map[string]any) (*OB11Response, error) {
|
||||
// 构建请求数据
|
||||
reqData := map[string]interface{}{
|
||||
reqData := map[string]any{
|
||||
"action": action,
|
||||
"params": params,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func (a *Adapter) convertToEvent(raw *RawEvent) (protocol.Event, error) {
|
||||
baseEvent := &protocol.BaseEvent{
|
||||
Timestamp: raw.Time,
|
||||
SelfID: strconv.FormatInt(raw.SelfID, 10),
|
||||
Data: make(map[string]interface{}),
|
||||
Data: make(map[string]any),
|
||||
}
|
||||
|
||||
switch raw.PostType {
|
||||
@@ -49,7 +49,7 @@ func (a *Adapter) convertMessageEvent(raw *RawEvent, base *protocol.BaseEvent) (
|
||||
}
|
||||
|
||||
if raw.Sender != nil {
|
||||
senderData := map[string]interface{}{
|
||||
senderData := map[string]any{
|
||||
"user_id": raw.Sender.UserID,
|
||||
"nickname": raw.Sender.Nickname,
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func (a *Adapter) convertMessageEvent(raw *RawEvent, base *protocol.BaseEvent) (
|
||||
}
|
||||
|
||||
if raw.Anonymous != nil {
|
||||
base.Data["anonymous"] = map[string]interface{}{
|
||||
base.Data["anonymous"] = map[string]any{
|
||||
"id": raw.Anonymous.ID,
|
||||
"name": raw.Anonymous.Name,
|
||||
"flag": raw.Anonymous.Flag,
|
||||
@@ -111,7 +111,7 @@ func (a *Adapter) convertNoticeEvent(raw *RawEvent, base *protocol.BaseEvent) (p
|
||||
case NoticeTypeGroupUpload:
|
||||
// 文件上传信息
|
||||
if raw.File != nil {
|
||||
base.Data["file"] = map[string]interface{}{
|
||||
base.Data["file"] = map[string]any{
|
||||
"id": raw.File.ID,
|
||||
"name": raw.File.Name,
|
||||
"size": raw.File.Size,
|
||||
@@ -156,12 +156,12 @@ func (a *Adapter) convertMetaEvent(raw *RawEvent, base *protocol.BaseEvent) (pro
|
||||
base.DetailType = raw.MetaType
|
||||
|
||||
if raw.Status != nil {
|
||||
statusData := map[string]interface{}{
|
||||
statusData := map[string]any{
|
||||
"online": raw.Status.Online,
|
||||
"good": raw.Status.Good,
|
||||
}
|
||||
if raw.Status.Stat != nil {
|
||||
statusData["stat"] = map[string]interface{}{
|
||||
statusData["stat"] = map[string]any{
|
||||
"packet_received": raw.Status.Stat.PacketReceived,
|
||||
"packet_sent": raw.Status.Stat.PacketSent,
|
||||
"packet_lost": raw.Status.Stat.PacketLost,
|
||||
@@ -183,7 +183,7 @@ func (a *Adapter) convertMetaEvent(raw *RawEvent, base *protocol.BaseEvent) (pro
|
||||
}
|
||||
|
||||
// parseMessageSegments 解析消息段
|
||||
func (a *Adapter) parseMessageSegments(message interface{}) ([]MessageSegment, error) {
|
||||
func (a *Adapter) parseMessageSegments(message any) ([]MessageSegment, error) {
|
||||
if message == nil {
|
||||
return nil, fmt.Errorf("message is nil")
|
||||
}
|
||||
@@ -193,7 +193,7 @@ func (a *Adapter) parseMessageSegments(message interface{}) ([]MessageSegment, e
|
||||
return []MessageSegment{
|
||||
{
|
||||
Type: SegmentTypeText,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"text": str,
|
||||
},
|
||||
},
|
||||
@@ -215,7 +215,7 @@ func (a *Adapter) parseMessageSegments(message interface{}) ([]MessageSegment, e
|
||||
}
|
||||
|
||||
// BuildMessage 构建OneBot11消息
|
||||
func BuildMessage(segments []MessageSegment) interface{} {
|
||||
func BuildMessage(segments []MessageSegment) any {
|
||||
if len(segments) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -235,7 +235,7 @@ func BuildTextMessage(text string) []MessageSegment {
|
||||
return []MessageSegment{
|
||||
{
|
||||
Type: SegmentTypeText,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"text": text,
|
||||
},
|
||||
},
|
||||
@@ -247,7 +247,7 @@ func BuildImageMessage(file string) []MessageSegment {
|
||||
return []MessageSegment{
|
||||
{
|
||||
Type: SegmentTypeImage,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": file,
|
||||
},
|
||||
},
|
||||
@@ -258,7 +258,7 @@ func BuildImageMessage(file string) []MessageSegment {
|
||||
func BuildAtMessage(userID int64) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeAt,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"qq": userID,
|
||||
},
|
||||
}
|
||||
@@ -268,7 +268,7 @@ func BuildAtMessage(userID int64) MessageSegment {
|
||||
func BuildReplyMessage(messageID int32) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeReply,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"id": messageID,
|
||||
},
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func BuildReplyMessage(messageID int32) MessageSegment {
|
||||
func BuildFaceMessage(faceID int) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeFace,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"id": faceID,
|
||||
},
|
||||
}
|
||||
@@ -288,7 +288,7 @@ func BuildFaceMessage(faceID int) MessageSegment {
|
||||
func BuildRecordMessage(file string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeRecord,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": file,
|
||||
},
|
||||
}
|
||||
@@ -298,7 +298,7 @@ func BuildRecordMessage(file string) MessageSegment {
|
||||
func BuildVideoMessage(file string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeVideo,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": file,
|
||||
},
|
||||
}
|
||||
@@ -308,7 +308,7 @@ func BuildVideoMessage(file string) MessageSegment {
|
||||
func BuildShareMessage(url, title string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeShare,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"url": url,
|
||||
"title": title,
|
||||
},
|
||||
@@ -319,7 +319,7 @@ func BuildShareMessage(url, title string) MessageSegment {
|
||||
func BuildLocationMessage(lat, lon float64, title, content string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeLocation,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"title": title,
|
||||
@@ -332,7 +332,7 @@ func BuildLocationMessage(lat, lon float64, title, content string) MessageSegmen
|
||||
func BuildMusicMessage(musicType, musicID string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeMusic,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"type": musicType,
|
||||
"id": musicID,
|
||||
},
|
||||
@@ -343,7 +343,7 @@ func BuildMusicMessage(musicType, musicID string) MessageSegment {
|
||||
func BuildCustomMusicMessage(url, audio, title, content, image string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeMusic,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"type": "custom",
|
||||
"url": url,
|
||||
"audio": audio,
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
// ConvertMessageChainToOB11 将通用消息链转换为 OneBot11 格式
|
||||
func ConvertMessageChainToOB11(chain protocol.MessageChain) interface{} {
|
||||
func ConvertMessageChainToOB11(chain protocol.MessageChain) any {
|
||||
if len(chain) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
// 文本消息段
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeText,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"text": seg.Data["text"],
|
||||
},
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
userID := seg.Data["user_id"]
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeAt,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"qq": userID,
|
||||
},
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
}
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeAt,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"qq": userID,
|
||||
},
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
}
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeImage,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": fileID,
|
||||
},
|
||||
}
|
||||
@@ -88,7 +88,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
}
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeRecord,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": fileID,
|
||||
},
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
}
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeRecord,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": fileID,
|
||||
},
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
}
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeVideo,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": fileID,
|
||||
},
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
messageID := seg.Data["message_id"]
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeReply,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"id": messageID,
|
||||
},
|
||||
}
|
||||
@@ -134,7 +134,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
faceID := seg.Data["id"]
|
||||
return &MessageSegment{
|
||||
Type: SegmentTypeFace,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"id": faceID,
|
||||
},
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func convertSegmentToOB11(seg protocol.MessageSegment) *MessageSegment {
|
||||
}
|
||||
|
||||
// ConvertOB11ToMessageChain 将 OneBot11 消息段转换为通用消息链
|
||||
func ConvertOB11ToMessageChain(ob11Message interface{}) (protocol.MessageChain, error) {
|
||||
func ConvertOB11ToMessageChain(ob11Message any) (protocol.MessageChain, error) {
|
||||
chain := protocol.MessageChain{}
|
||||
|
||||
// 如果是字符串,转换为文本消息段
|
||||
@@ -167,11 +167,11 @@ func ConvertOB11ToMessageChain(ob11Message interface{}) (protocol.MessageChain,
|
||||
}
|
||||
|
||||
// 如果是接口数组,尝试转换
|
||||
if segments, ok := ob11Message.([]interface{}); ok {
|
||||
if segments, ok := ob11Message.([]any); ok {
|
||||
for _, seg := range segments {
|
||||
if segMap, ok := seg.(map[string]interface{}); ok {
|
||||
if segMap, ok := seg.(map[string]any); ok {
|
||||
segType, _ := segMap["type"].(string)
|
||||
segData, _ := segMap["data"].(map[string]interface{})
|
||||
segData, _ := segMap["data"].(map[string]any)
|
||||
genericSeg := convertOB11SegmentToGeneric(MessageSegment{
|
||||
Type: segType,
|
||||
Data: segData,
|
||||
@@ -211,7 +211,7 @@ func convertOB11SegmentToGeneric(seg MessageSegment) protocol.MessageSegment {
|
||||
}
|
||||
return protocol.MessageSegment{
|
||||
Type: protocol.SegmentTypeVoice,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file_id": fileID,
|
||||
},
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func convertOB11SegmentToGeneric(seg MessageSegment) protocol.MessageSegment {
|
||||
case SegmentTypeFace:
|
||||
return protocol.MessageSegment{
|
||||
Type: protocol.SegmentTypeFace,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"id": seg.Data["id"],
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2,32 +2,32 @@ package onebot11
|
||||
|
||||
// RawEvent OneBot11原始事件
|
||||
type RawEvent struct {
|
||||
Time int64 `json:"time"`
|
||||
SelfID int64 `json:"self_id"`
|
||||
PostType string `json:"post_type"`
|
||||
MessageType string `json:"message_type,omitempty"`
|
||||
SubType string `json:"sub_type,omitempty"`
|
||||
MessageID int32 `json:"message_id,omitempty"`
|
||||
UserID int64 `json:"user_id,omitempty"`
|
||||
GroupID int64 `json:"group_id,omitempty"`
|
||||
Message interface{} `json:"message,omitempty"`
|
||||
RawMessage string `json:"raw_message,omitempty"`
|
||||
Font int32 `json:"font,omitempty"`
|
||||
Sender *Sender `json:"sender,omitempty"`
|
||||
Anonymous *Anonymous `json:"anonymous,omitempty"`
|
||||
NoticeType string `json:"notice_type,omitempty"`
|
||||
OperatorID int64 `json:"operator_id,omitempty"`
|
||||
Duration int64 `json:"duration,omitempty"`
|
||||
RequestType string `json:"request_type,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Flag string `json:"flag,omitempty"`
|
||||
MetaType string `json:"meta_event_type,omitempty"`
|
||||
Status *Status `json:"status,omitempty"`
|
||||
Interval int64 `json:"interval,omitempty"`
|
||||
File *FileInfo `json:"file,omitempty"` // 群文件上传信息
|
||||
TargetID int64 `json:"target_id,omitempty"` // 戳一戳、红包运气王目标ID
|
||||
HonorType string `json:"honor_type,omitempty"` // 群荣誉类型
|
||||
Extra map[string]interface{} `json:"-"`
|
||||
Time int64 `json:"time"`
|
||||
SelfID int64 `json:"self_id"`
|
||||
PostType string `json:"post_type"`
|
||||
MessageType string `json:"message_type,omitempty"`
|
||||
SubType string `json:"sub_type,omitempty"`
|
||||
MessageID int32 `json:"message_id,omitempty"`
|
||||
UserID int64 `json:"user_id,omitempty"`
|
||||
GroupID int64 `json:"group_id,omitempty"`
|
||||
Message any `json:"message,omitempty"`
|
||||
RawMessage string `json:"raw_message,omitempty"`
|
||||
Font int32 `json:"font,omitempty"`
|
||||
Sender *Sender `json:"sender,omitempty"`
|
||||
Anonymous *Anonymous `json:"anonymous,omitempty"`
|
||||
NoticeType string `json:"notice_type,omitempty"`
|
||||
OperatorID int64 `json:"operator_id,omitempty"`
|
||||
Duration int64 `json:"duration,omitempty"`
|
||||
RequestType string `json:"request_type,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
Flag string `json:"flag,omitempty"`
|
||||
MetaType string `json:"meta_event_type,omitempty"`
|
||||
Status *Status `json:"status,omitempty"`
|
||||
Interval int64 `json:"interval,omitempty"`
|
||||
File *FileInfo `json:"file,omitempty"` // 群文件上传信息
|
||||
TargetID int64 `json:"target_id,omitempty"` // 戳一戳、红包运气王目标ID
|
||||
HonorType string `json:"honor_type,omitempty"` // 群荣誉类型
|
||||
Extra map[string]any `json:"-"`
|
||||
}
|
||||
|
||||
// Sender 发送者信息
|
||||
@@ -79,23 +79,23 @@ type Stat struct {
|
||||
|
||||
// OB11Action OneBot11动作
|
||||
type OB11Action struct {
|
||||
Action string `json:"action"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Echo string `json:"echo,omitempty"`
|
||||
Action string `json:"action"`
|
||||
Params map[string]any `json:"params"`
|
||||
Echo string `json:"echo,omitempty"`
|
||||
}
|
||||
|
||||
// OB11Response OneBot11响应
|
||||
type OB11Response struct {
|
||||
Status string `json:"status"`
|
||||
RetCode int `json:"retcode"`
|
||||
Data map[string]interface{} `json:"data,omitempty"`
|
||||
Echo string `json:"echo,omitempty"`
|
||||
Status string `json:"status"`
|
||||
RetCode int `json:"retcode"`
|
||||
Data map[string]any `json:"data,omitempty"`
|
||||
Echo string `json:"echo,omitempty"`
|
||||
}
|
||||
|
||||
// MessageSegment 消息段
|
||||
type MessageSegment struct {
|
||||
Type string `json:"type"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
Type string `json:"type"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
|
||||
// 消息段类型常量
|
||||
|
||||
@@ -149,22 +149,9 @@ func ProvideMilkyBots(cfg *config.Config, logger *zap.Logger, eventBus *engine.E
|
||||
bot := milky.NewBot(botCfg.ID, milkyCfg, eventBus, wsManager, logger)
|
||||
botManager.Add(bot)
|
||||
|
||||
// 注意:不在这里启动连接,由 RegisterLifecycleHooks 中的 botManager.StartAll() 统一启动
|
||||
// 这样避免重复启动的问题
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
logger.Info("Starting Milky bot", zap.String("bot_id", botCfg.ID))
|
||||
// 在后台启动连接,失败时只记录错误,不终止应用
|
||||
go func() {
|
||||
if err := bot.Connect(context.Background()); err != nil {
|
||||
logger.Error("Failed to connect Milky bot, will retry in background",
|
||||
zap.String("bot_id", botCfg.ID),
|
||||
zap.Error(err))
|
||||
// 可以在这里实现重试逻辑
|
||||
} else {
|
||||
logger.Info("Milky bot connected successfully", zap.String("bot_id", botCfg.ID))
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
logger.Info("Stopping Milky bot", zap.String("bot_id", botCfg.ID))
|
||||
return bot.Disconnect(ctx)
|
||||
@@ -200,22 +187,9 @@ func ProvideOneBot11Bots(cfg *config.Config, logger *zap.Logger, wsManager *net.
|
||||
bot := onebot11.NewBot(botCfg.ID, ob11Cfg, logger, wsManager, eventBus)
|
||||
botManager.Add(bot)
|
||||
|
||||
// 注意:不在这里启动连接,由 RegisterLifecycleHooks 中的 botManager.StartAll() 统一启动
|
||||
// 这样避免重复启动的问题
|
||||
lc.Append(fx.Hook{
|
||||
OnStart: func(ctx context.Context) error {
|
||||
logger.Info("Starting OneBot11 bot", zap.String("bot_id", botCfg.ID))
|
||||
// 在后台启动连接,失败时只记录错误,不终止应用
|
||||
go func() {
|
||||
if err := bot.Connect(context.Background()); err != nil {
|
||||
logger.Error("Failed to connect OneBot11 bot, will retry in background",
|
||||
zap.String("bot_id", botCfg.ID),
|
||||
zap.Error(err))
|
||||
// 可以在这里实现重试逻辑
|
||||
} else {
|
||||
logger.Info("OneBot11 bot connected successfully", zap.String("bot_id", botCfg.ID))
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
},
|
||||
OnStop: func(ctx context.Context) error {
|
||||
logger.Info("Stopping OneBot11 bot", zap.String("bot_id", botCfg.ID))
|
||||
return bot.Disconnect(ctx)
|
||||
|
||||
@@ -3,6 +3,7 @@ package engine
|
||||
import (
|
||||
"context"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"sort"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -238,8 +239,7 @@ func (d *Dispatcher) handleEvent(ctx context.Context, event protocol.Event) {
|
||||
func (d *Dispatcher) createHandlerChain(ctx context.Context, event protocol.Event) func(context.Context, protocol.Event) {
|
||||
return func(ctx context.Context, e protocol.Event) {
|
||||
d.mu.RLock()
|
||||
handlers := make([]protocol.EventHandler, len(d.handlers))
|
||||
copy(handlers, d.handlers)
|
||||
handlers := slices.Clone(d.handlers)
|
||||
d.mu.RUnlock()
|
||||
|
||||
for i, handler := range handlers {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"slices"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
@@ -63,8 +64,7 @@ func NewEventBus(logger *zap.Logger, bufferSize int) *EventBus {
|
||||
|
||||
// Start 启动事件总线
|
||||
func (eb *EventBus) Start() {
|
||||
eb.wg.Add(1)
|
||||
go eb.dispatch()
|
||||
eb.wg.Go(eb.dispatch)
|
||||
eb.logger.Info("Event bus started")
|
||||
}
|
||||
|
||||
@@ -193,8 +193,6 @@ func (eb *EventBus) Unsubscribe(eventType protocol.EventType, ch chan protocol.E
|
||||
|
||||
// dispatch 分发事件到订阅者
|
||||
func (eb *EventBus) dispatch() {
|
||||
defer eb.wg.Done()
|
||||
|
||||
eb.logger.Info("Event bus dispatch loop started")
|
||||
|
||||
for {
|
||||
@@ -220,8 +218,7 @@ func (eb *EventBus) dispatchEvent(event protocol.Event) {
|
||||
key := string(event.GetType())
|
||||
subs := eb.subscriptions[key]
|
||||
// 复制订阅者列表避免锁竞争
|
||||
subsCopy := make([]*Subscription, len(subs))
|
||||
copy(subsCopy, subs)
|
||||
subsCopy := slices.Clone(subs)
|
||||
eb.mu.RUnlock()
|
||||
|
||||
eb.logger.Info("Dispatching event",
|
||||
|
||||
@@ -260,14 +260,14 @@ func (m *MetricsMiddleware) Process(ctx context.Context, event protocol.Event, n
|
||||
}
|
||||
|
||||
// GetMetrics 获取指标
|
||||
func (m *MetricsMiddleware) GetMetrics() map[string]interface{} {
|
||||
func (m *MetricsMiddleware) GetMetrics() map[string]any {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
|
||||
metrics := make(map[string]interface{})
|
||||
metrics := make(map[string]any)
|
||||
for eventType, count := range m.eventCounts {
|
||||
avgTime := m.eventTimes[eventType] / time.Duration(count)
|
||||
metrics[eventType] = map[string]interface{}{
|
||||
metrics[eventType] = map[string]any{
|
||||
"count": count,
|
||||
"avg_time": avgTime.String(),
|
||||
}
|
||||
|
||||
@@ -599,12 +599,12 @@ func OnlyToMe() HandlerMiddleware {
|
||||
selfID := event.GetSelfID()
|
||||
|
||||
// 检查消息段中是否包含@机器人的消息
|
||||
if segments, ok := data["message_segments"].([]interface{}); ok {
|
||||
if segments, ok := data["message_segments"].([]any); ok {
|
||||
for _, seg := range segments {
|
||||
if segMap, ok := seg.(map[string]interface{}); ok {
|
||||
if segMap, ok := seg.(map[string]any); ok {
|
||||
segType, _ := segMap["type"].(string)
|
||||
if segType == "at" || segType == "mention" {
|
||||
segData, _ := segMap["data"].(map[string]interface{})
|
||||
segData, _ := segMap["data"].(map[string]any)
|
||||
// 检查是否@了机器人
|
||||
if userID, ok := segData["user_id"]; ok {
|
||||
if userIDStr := fmt.Sprintf("%v", userID); userIDStr == selfID {
|
||||
|
||||
@@ -329,15 +329,13 @@ func (j *IntervalJob) Start(ctx context.Context) error {
|
||||
j.nextRun = time.Now().Add(j.interval)
|
||||
j.mu.Unlock()
|
||||
|
||||
j.wg.Add(1)
|
||||
go j.run()
|
||||
j.wg.Go(j.run)
|
||||
|
||||
j.logger.Info("Interval job started", zap.Duration("interval", j.interval))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *IntervalJob) run() {
|
||||
defer j.wg.Done()
|
||||
|
||||
// 立即执行一次(可选,根据需求调整)
|
||||
// if err := j.handler(j.ctx); err != nil {
|
||||
@@ -428,16 +426,13 @@ func (j *OnceJob) Start(ctx context.Context) error {
|
||||
j.nextRun = time.Now().Add(j.delay)
|
||||
j.mu.Unlock()
|
||||
|
||||
j.wg.Add(1)
|
||||
go j.run()
|
||||
j.wg.Go(j.run)
|
||||
|
||||
j.logger.Info("Once job started", zap.Duration("delay", j.delay))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (j *OnceJob) run() {
|
||||
defer j.wg.Done()
|
||||
|
||||
select {
|
||||
case <-j.timer.C:
|
||||
if err := j.handler(j.ctx); err != nil {
|
||||
|
||||
@@ -192,7 +192,7 @@ func handleMCSBindCommand(ctx context.Context, event protocol.Event, botManager
|
||||
|
||||
action := &protocol.BaseAction{
|
||||
Type: protocol.ActionTypeSendGroupMessage,
|
||||
Params: map[string]interface{}{
|
||||
Params: map[string]any{
|
||||
"group_id": groupID,
|
||||
"message": errorMsg,
|
||||
},
|
||||
|
||||
@@ -76,7 +76,7 @@ func Ping(host string, port int, timeout time.Duration) (*ServerStatus, error) {
|
||||
}
|
||||
|
||||
// 解析 JSON 响应
|
||||
var statusData map[string]interface{}
|
||||
var statusData map[string]any
|
||||
if err := json.Unmarshal([]byte(statusJSON), &statusData); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse JSON: %w", err)
|
||||
}
|
||||
@@ -88,14 +88,14 @@ func Ping(host string, port int, timeout time.Duration) (*ServerStatus, error) {
|
||||
}
|
||||
|
||||
// 解析 description
|
||||
if desc, ok := statusData["description"].(map[string]interface{}); ok {
|
||||
if desc, ok := statusData["description"].(map[string]any); ok {
|
||||
if text, ok := desc["text"].(string); ok {
|
||||
status.Description = text
|
||||
} else if text, ok := desc["extra"].([]interface{}); ok && len(text) > 0 {
|
||||
} else if text, ok := desc["extra"].([]any); ok && len(text) > 0 {
|
||||
// 处理 extra 数组
|
||||
var descText strings.Builder
|
||||
for _, item := range text {
|
||||
if itemMap, ok := item.(map[string]interface{}); ok {
|
||||
if itemMap, ok := item.(map[string]any); ok {
|
||||
if text, ok := itemMap["text"].(string); ok {
|
||||
descText.WriteString(text)
|
||||
}
|
||||
@@ -108,7 +108,7 @@ func Ping(host string, port int, timeout time.Duration) (*ServerStatus, error) {
|
||||
}
|
||||
|
||||
// 解析 version
|
||||
if version, ok := statusData["version"].(map[string]interface{}); ok {
|
||||
if version, ok := statusData["version"].(map[string]any); ok {
|
||||
if name, ok := version["name"].(string); ok {
|
||||
status.Version.Name = name
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func Ping(host string, port int, timeout time.Duration) (*ServerStatus, error) {
|
||||
}
|
||||
|
||||
// 解析 players
|
||||
if players, ok := statusData["players"].(map[string]interface{}); ok {
|
||||
if players, ok := statusData["players"].(map[string]any); ok {
|
||||
if online, ok := players["online"].(float64); ok {
|
||||
status.Players.Online = int(online)
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func handleWelcomeEvent(ctx context.Context, event protocol.Event, botManager *p
|
||||
zap.Any("user_id", userID))
|
||||
|
||||
// 获取操作者ID(邀请者或审批者)
|
||||
var operatorID interface{}
|
||||
var operatorID any
|
||||
if opID, exists := data["operator_id"]; exists {
|
||||
operatorID = opID
|
||||
}
|
||||
@@ -95,7 +95,7 @@ func handleWelcomeEvent(ctx context.Context, event protocol.Event, botManager *p
|
||||
|
||||
action := &protocol.BaseAction{
|
||||
Type: protocol.ActionTypeSendGroupMessage,
|
||||
Params: map[string]interface{}{
|
||||
Params: map[string]any{
|
||||
"group_id": groupID,
|
||||
"message": welcomeChain,
|
||||
},
|
||||
@@ -285,14 +285,14 @@ const welcomeTemplate = `
|
||||
`
|
||||
|
||||
// buildWelcomeMessage 构建欢迎消息链(使用HTML模板渲染图片)
|
||||
func buildWelcomeMessage(ctx context.Context, userID, operatorID interface{}, subType string, logger *zap.Logger) (protocol.MessageChain, error) {
|
||||
func buildWelcomeMessage(ctx context.Context, userID, operatorID any, subType string, logger *zap.Logger) (protocol.MessageChain, error) {
|
||||
logger.Debug("Starting to build welcome message",
|
||||
zap.Any("user_id", userID),
|
||||
zap.Any("operator_id", operatorID),
|
||||
zap.String("sub_type", subType))
|
||||
|
||||
// 准备模板数据
|
||||
data := map[string]interface{}{
|
||||
data := map[string]any{
|
||||
"UserID": fmt.Sprintf("%v", userID),
|
||||
}
|
||||
logger.Debug("Template data prepared", zap.Any("data", data))
|
||||
|
||||
@@ -6,9 +6,9 @@ type Action interface {
|
||||
// GetType 获取动作类型
|
||||
GetType() ActionType
|
||||
// GetParams 获取动作参数
|
||||
GetParams() map[string]interface{}
|
||||
GetParams() map[string]any
|
||||
// Execute 执行动作
|
||||
Execute(ctx interface{}) (map[string]interface{}, error)
|
||||
Execute(ctx any) (map[string]any, error)
|
||||
}
|
||||
|
||||
// ActionType 动作类型
|
||||
@@ -27,20 +27,20 @@ const (
|
||||
ActionTypeGetGroupMemberList ActionType = "get_group_member_list"
|
||||
|
||||
// 群组相关动作
|
||||
ActionTypeSetGroupKick ActionType = "set_group_kick"
|
||||
ActionTypeSetGroupBan ActionType = "set_group_ban"
|
||||
ActionTypeSetGroupAdmin ActionType = "set_group_admin"
|
||||
ActionTypeSetGroupWholeBan ActionType = "set_group_whole_ban"
|
||||
ActionTypeSetGroupKick ActionType = "set_group_kick"
|
||||
ActionTypeSetGroupBan ActionType = "set_group_ban"
|
||||
ActionTypeSetGroupAdmin ActionType = "set_group_admin"
|
||||
ActionTypeSetGroupWholeBan ActionType = "set_group_whole_ban"
|
||||
|
||||
// 其他动作
|
||||
ActionTypeGetStatus ActionType = "get_status"
|
||||
ActionTypeGetVersion ActionType = "get_version"
|
||||
ActionTypeGetStatus ActionType = "get_status"
|
||||
ActionTypeGetVersion ActionType = "get_version"
|
||||
)
|
||||
|
||||
// BaseAction 基础动作结构
|
||||
type BaseAction struct {
|
||||
Type ActionType `json:"type"`
|
||||
Params map[string]interface{} `json:"params"`
|
||||
Type ActionType `json:"type"`
|
||||
Params map[string]any `json:"params"`
|
||||
}
|
||||
|
||||
// GetType 获取动作类型
|
||||
@@ -49,19 +49,19 @@ func (a *BaseAction) GetType() ActionType {
|
||||
}
|
||||
|
||||
// GetParams 获取动作参数
|
||||
func (a *BaseAction) GetParams() map[string]interface{} {
|
||||
func (a *BaseAction) GetParams() map[string]any {
|
||||
return a.Params
|
||||
}
|
||||
|
||||
// Execute 执行动作(需子类实现)
|
||||
func (a *BaseAction) Execute(ctx interface{}) (map[string]interface{}, error) {
|
||||
func (a *BaseAction) Execute(ctx any) (map[string]any, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
// SendPrivateMessageAction 发送私聊消息动作
|
||||
type SendPrivateMessageAction struct {
|
||||
BaseAction
|
||||
UserID string `json:"user_id"`
|
||||
UserID string `json:"user_id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ func (b *BaseBotInstance) IsConnected() bool {
|
||||
}
|
||||
|
||||
// SendAction 发送动作
|
||||
func (b *BaseBotInstance) SendAction(ctx context.Context, action Action) (map[string]interface{}, error) {
|
||||
func (b *BaseBotInstance) SendAction(ctx context.Context, action Action) (map[string]any, error) {
|
||||
return b.protocol.SendAction(ctx, action)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ type Event interface {
|
||||
// GetSelfID 获取机器人自身ID
|
||||
GetSelfID() string
|
||||
// GetData 获取事件数据
|
||||
GetData() map[string]interface{}
|
||||
GetData() map[string]any
|
||||
// Reply 在消息发生的群/私聊进行回复
|
||||
Reply(ctx context.Context, botManager *BotManager, logger *zap.Logger, message MessageChain) error
|
||||
// ReplyText 在消息发生的群/私聊进行文本回复(便捷方法)
|
||||
@@ -44,12 +44,12 @@ type Event interface {
|
||||
|
||||
// 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"`
|
||||
Type EventType `json:"type"`
|
||||
DetailType string `json:"detail_type"`
|
||||
SubType string `json:"sub_type,omitzero"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
SelfID string `json:"self_id"`
|
||||
Data map[string]any `json:"data"`
|
||||
}
|
||||
|
||||
// GetType 获取事件类型
|
||||
@@ -78,7 +78,7 @@ func (e *BaseEvent) GetSelfID() string {
|
||||
}
|
||||
|
||||
// GetData 获取事件数据
|
||||
func (e *BaseEvent) GetData() map[string]interface{} {
|
||||
func (e *BaseEvent) GetData() map[string]any {
|
||||
return e.Data
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func (e *BaseEvent) Reply(ctx context.Context, botManager *BotManager, logger *z
|
||||
if e.GetDetailType() == "private" {
|
||||
action = &BaseAction{
|
||||
Type: ActionTypeSendPrivateMessage,
|
||||
Params: map[string]interface{}{
|
||||
Params: map[string]any{
|
||||
"user_id": userID,
|
||||
"message": message,
|
||||
},
|
||||
@@ -114,7 +114,7 @@ func (e *BaseEvent) Reply(ctx context.Context, botManager *BotManager, logger *z
|
||||
// 群聊或其他有 group_id 的事件
|
||||
action = &BaseAction{
|
||||
Type: ActionTypeSendGroupMessage,
|
||||
Params: map[string]interface{}{
|
||||
Params: map[string]any{
|
||||
"group_id": groupID,
|
||||
"message": message,
|
||||
},
|
||||
@@ -151,15 +151,15 @@ type MessageEvent struct {
|
||||
BaseEvent
|
||||
MessageID string `json:"message_id"`
|
||||
Message string `json:"message"`
|
||||
AltText string `json:"alt_text,omitempty"`
|
||||
AltText string `json:"alt_text,omitzero"`
|
||||
}
|
||||
|
||||
// NoticeEvent 通知事件
|
||||
type NoticeEvent struct {
|
||||
BaseEvent
|
||||
GroupID string `json:"group_id,omitempty"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
Operator string `json:"operator,omitempty"`
|
||||
GroupID string `json:"group_id,omitzero"`
|
||||
UserID string `json:"user_id,omitzero"`
|
||||
Operator string `json:"operator,omitzero"`
|
||||
}
|
||||
|
||||
// RequestEvent 请求事件
|
||||
|
||||
@@ -18,7 +18,7 @@ type Protocol interface {
|
||||
// IsConnected 检查连接状态
|
||||
IsConnected() bool
|
||||
// SendAction 发送动作
|
||||
SendAction(ctx context.Context, action Action) (map[string]interface{}, error)
|
||||
SendAction(ctx context.Context, action Action) (map[string]any, error)
|
||||
// HandleEvent 处理事件
|
||||
HandleEvent(ctx context.Context, event Event) error
|
||||
// GetSelfID 获取机器人自身ID
|
||||
|
||||
@@ -5,8 +5,8 @@ import "fmt"
|
||||
// MessageSegment 消息段(基于 OneBot12 设计)
|
||||
// 通用消息段结构,适配器负责转换为具体协议格式
|
||||
type MessageSegment struct {
|
||||
Type string `json:"type"` // 消息段类型
|
||||
Data map[string]interface{} `json:"data"` // 消息段数据
|
||||
Type string `json:"type"` // 消息段类型
|
||||
Data map[string]any `json:"data"` // 消息段数据
|
||||
}
|
||||
|
||||
// MessageChain 消息链(基于 OneBot12 设计)
|
||||
@@ -33,17 +33,17 @@ const (
|
||||
func NewTextSegment(text string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeText,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"text": text,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewMentionSegment 创建@提及消息段(OneBot12 标准)
|
||||
func NewMentionSegment(userID interface{}) MessageSegment {
|
||||
func NewMentionSegment(userID any) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeMention,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"user_id": userID,
|
||||
},
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func NewMentionSegment(userID interface{}) MessageSegment {
|
||||
func NewImageSegment(fileID string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeImage,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file_id": fileID,
|
||||
},
|
||||
}
|
||||
@@ -63,17 +63,17 @@ func NewImageSegment(fileID string) MessageSegment {
|
||||
func NewImageSegmentFromBase64(base64Data string) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeImage,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"file": fmt.Sprintf("base64://%s", base64Data),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NewReplySegment 创建回复消息段
|
||||
func NewReplySegment(messageID interface{}) MessageSegment {
|
||||
func NewReplySegment(messageID any) MessageSegment {
|
||||
return MessageSegment{
|
||||
Type: SegmentTypeReply,
|
||||
Data: map[string]interface{}{
|
||||
Data: map[string]any{
|
||||
"message_id": messageID,
|
||||
},
|
||||
}
|
||||
@@ -95,7 +95,7 @@ func (mc MessageChain) AppendText(text string) MessageChain {
|
||||
}
|
||||
|
||||
// AppendMention 追加@提及到消息链
|
||||
func (mc MessageChain) AppendMention(userID interface{}) MessageChain {
|
||||
func (mc MessageChain) AppendMention(userID any) MessageChain {
|
||||
return mc.Append(NewMentionSegment(userID))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user