package snowluma import ( "context" "sync" ) // Context 是交给处理函数的上下文:携带事件,并把 API 调用路由到 // 产生该事件的连接;HTTP 上报模式下 Reply/Approve 走快速操作。 type Context struct { // API 内嵌全部类型化 API 方法,调用时路由到本事件的来源连接。 API Bot *Client Event *Event quick *quickBox segOnce sync.Once segs []Segment segErr error } // Call 在本事件来源的连接上执行任意 action(未收录 API 的兜底入口)。 func (c *Context) Call(ctx context.Context, action string, params any, out any) error { return c.API.call.Call(ctx, action, params, out) } // Segments 返回消息事件的消息段数组(CQ 码字符串也会被解析),结果带缓存。 func (c *Context) Segments() []Segment { c.segOnce.Do(func() { if c.Event.Message != nil { c.segs, c.segErr = DecodeMessage(c.Event.Message.Message) } }) if c.segErr != nil { return nil } return c.segs } // PlainText 返回消息里所有 text 段拼接出的文本。 func (c *Context) PlainText() string { return SegmentsText(c.Segments()) } // SelfID 返回产生本事件的机器人 QQ 号。 func (c *Context) SelfID() int64 { return c.Event.SelfID } // IsGroup 判断是否群聊消息事件。 func (c *Context) IsGroup() bool { return c.Event.Message != nil && c.Event.Message.MessageType == "group" } // IsPrivate 判断是否私聊消息事件。 func (c *Context) IsPrivate() bool { return c.Event.Message != nil && c.Event.Message.MessageType == "private" } // IsSelfMessage 判断是否 bot 自己发出的消息(message_sent)。 func (c *Context) IsSelfMessage() bool { return c.Event.PostType == "message_sent" } // HasAtMe 判断消息是否 @ 了机器人(不含 @全体)。 func (c *Context) HasAtMe() bool { me := c.SelfID() for _, s := range c.Segments() { if s.Type == "at" && intOf(s.Data["qq"]) == me { return true } } return false } // Reply 回复当前消息:群聊发 send_group_msg,私聊发 send_private_msg。 // HTTP 上报模式下改为随响应返回快速操作 reply(此时 message_id 返回 0)。 func (c *Context) Reply(message ...Segment) (MessageID, error) { ev := c.Event if ev.Message == nil { return 0, errReplyNotMessage } if c.quick != nil { c.quick.set("reply", message) return 0, nil } if ev.Message.MessageType == "group" { return c.SendGroupMsg(nil, ev.Message.GroupID, message...) } return c.SendPrivateMsg(nil, ev.Message.UserID, message...) } // ReplyText 回复一段纯文本。 func (c *Context) ReplyText(text string) (MessageID, error) { return c.Reply(Text(text)) } // Recall 撤回触发本事件的那条消息(需要 bot 有管理员权限/为私聊双方)。 func (c *Context) Recall() error { if c.Event.Message == nil { return errReplyNotMessage } return c.DeleteMsg(nil, c.Event.Message.MessageID) } // Approve 同意当前好友/群请求。 func (c *Context) Approve() error { return c.approveRequest(true, "") } // Reject 拒绝当前好友/群请求,reason 为拒绝理由(好友请求则为验证备注)。 func (c *Context) Reject(reason string) error { return c.approveRequest(false, reason) } func (c *Context) approveRequest(approve bool, text string) error { ev := c.Event if ev.Request == nil { return errRequestOnly } if c.quick != nil { c.quick.set("approve", approve) if text != "" { if ev.Request.RequestType == "friend" { c.quick.set("remark", text) } else { c.quick.set("reason", text) } } return nil } if ev.Request.RequestType == "friend" { return c.SetFriendAddRequest(nil, ev.Request.Flag, approve, text) } return c.SetGroupAddRequest(nil, ev.Request.Flag, ev.Request.SubType, approve, text) } var ( errReplyNotMessage = &frameError{"reply is only available for message events"} errRequestOnly = &frameError{"approve/reject is only available for request events"} ) type frameError struct{ msg string } func (e *frameError) Error() string { return "snowluma: " + e.msg } // MessageID 是 OneBot 消息 ID。 type MessageID = int64