Appearance
事件类型参考
事件分四个体系:18 种接收事件(event/receive/,网关推送 + 2 个生命周期就绪事件)、28 种发送事件(event/send/,操作前触发、可取消)、28 种后置事件(event/send/,操作成功后触发、不可取消,与发送事件一一配对)与 3 种框架事件(event/ 根包,心跳与插件生命周期)。
事件层级
Event (抽象基类,含 metadata 系统)
├── ReceiveEvent (抽象,携带 Session 快照)
│ ├── ReadyEvent # 每分片连接就绪(仅 WebSocket 模式,网关 READY)
│ ├── WebhookReadyEvent # Webhook 回调服务就绪(仅 Webhook 模式,触发一次)
│ ├── ResumedEvent # 断线重连会话恢复(仅 WebSocket 模式)
│ ├── GroupMessageEvent # 群普通消息
│ ├── GroupAtMessageEvent # 群 @ 消息
│ ├── C2CMessageEvent # 私聊消息
│ ├── InteractionEvent # 互动事件(按钮点击等)
│ ├── FriendAddEvent # 好友新增
│ ├── FriendDelEvent # 好友删除
│ ├── GroupAddRobotEvent # 机器人被加入群
│ ├── GroupDelRobotEvent # 机器人被移出群
│ ├── GroupMemberAddEvent # 群成员增加
│ ├── GroupMemberRemoveEvent # 群成员移除
│ ├── GroupMsgReceiveEvent # 群消息接收开关:开启
│ ├── GroupMsgRejectEvent # 群消息接收开关:关闭
│ ├── C2CMsgReceiveEvent # 私聊消息接收开关:开启
│ ├── C2CMsgRejectEvent # 私聊消息接收开关:关闭
│ └── GroupJoinRequestEvent # 用户申请入群
├── SendEvent (抽象,可取消)
│ ├── GroupMessageSendEvent # 群消息发送前
│ ├── C2CMessageSendEvent # 私聊消息发送前
│ ├── C2CStreamMessageSendEvent # 私聊流式消息发送前
│ ├── GroupFileSendEvent # 群文件发送前(uploadGroupFile)
│ ├── C2CFileSendEvent # 私聊文件发送前(uploadC2CFile)
│ ├── GroupUploadPrepareEvent # 群分片预上传前
│ ├── GroupUploadPartFinishEvent # 群分片确认前
│ ├── GroupUploadChunkedEvent # 群分片合并前
│ ├── C2CUploadPrepareEvent # 私聊分片预上传前
│ ├── C2CUploadPartFinishEvent # 私聊分片确认前
│ ├── C2CUploadChunkedEvent # 私聊分片合并前
│ ├── GroupMessageDeleteEvent # 撤回群消息前
│ ├── C2CMessageDeleteEvent # 撤回私聊消息前
│ ├── InteractionRespondEvent # 互动响应前
│ ├── GroupMemberMuteEvent # 群成员禁言前
│ ├── GroupMemberBatchRemoveEvent # 批量移除群成员前
│ ├── GroupMemberBlacklistEvent # 群黑名单操作前
│ ├── JoinRequestApproveEvent # 入群申请批准前
│ ├── JoinRequestDeclineEvent # 入群申请拒绝前
│ ├── UrlLinkGenerateEvent # 分享链接生成前
│ ├── MenuUpdateEvent # C2C 菜单修改前
│ ├── PanelCreateEvent # 指令面板创建前
│ ├── PanelUpdateEvent # 指令面板修改前
│ ├── PanelDeleteEvent # 指令面板删除前
│ ├── PanelTargetUpdateEvent # 指令面板关联对象修改前
│ ├── MediaImagePrepareEvent # 图片素材申请上传前
│ ├── MediaImageCompleteEvent # 图片素材完成上传前
│ └── MediaImageDeleteEvent # 图片素材删除前
│
├── NotifyEvent (抽象,不可取消 / 仅成功触发)
│ └─ 与上面 28 个 SendEvent 一一配对的 28 个 XxxNotifyEvent
│
├── HeartbeatEvent # 心跳发出 / 收到确认(仅 WebSocket)
├── PluginEnableEvent # 插件启用完成
└── PluginDisableEvent # 插件禁用完成后置事件命名规则固定为前置事件名 + Notify,清单见 后置事件 一节。
ReceiveEvent 公共方法(会话快照)
所有接收事件在创建时对 Session 做只读快照,事件自带连接上下文:
| 方法 | 返回类型 | 说明 |
|---|---|---|
getSessionId() | String | 会话 ID(断线重连恢复用,首连 READY 前为空串) |
getLastSeq() | int | 触发事件时的最后消息序列号 |
getIntent() | int | 订阅事件的意图位掩码 |
getUrl() | String | WebSocket 网关地址 |
getHeartbeatInterval() | int | 心跳间隔(毫秒) |
getShardId() | int | 当前分片 ID(从 0 开始) |
getShardCount() | int | 总分片数(1 = 未分片) |
nextReplySeq() | int | 获取下一个被动回复序列号(原子操作,用于 msg_seq 去重) |
分片
多分片时每个连接各自触发事件,用 getShardId() 区分来源。ReadyEvent 每片触发一次(仅 WebSocket 模式),全局初始化只在 getShardId() == 0 时做——插件做「机器人上线」初始化更推荐实现 Plugin.onActive()(两模式统一触发一次,无需判断分片)。
群消息事件
GroupAtMessageEvent
用户 @机器人 时触发。事件名:GROUP_AT_MESSAGE_CREATE
| 方法 | 返回类型 | 说明 |
|---|---|---|
getId() | String | 消息 ID(用于回复/撤回) |
getAuthor() | User | 发送者信息(可能为 null) |
getContent() | String | 文本内容(已去除 @ 前缀) |
getGroupOpenid() | String | 群 OpenID |
getTimestamp() | Timestamp | 事件时间 |
getMessageType() | int | 0=普通, 3=结构化卡片, 101=并行消息, 102=聊天记录, 103=引用 |
getAttachments() | List<MessageAttachment> | 附件列表 |
getMessageScene() | MessageScene | 消息场景上下文(含 msg_idx 用于引用回复/去重) |
getArkData() | ARKData | 结构化卡片数据(message_type=3 时有值) |
getMsgElements() | List<MsgElement> | 消息元素列表(引用消息时有值) |
getMentions() | List<User> | 被 @ 的用户列表 |
User 记录
群聊与单聊共用同一结构:单聊场景 userOpenid() 有值,群聊场景 memberOpenid() / memberRole() 有值。
java
public record User(
String id, // 用户唯一标识(OpenID 格式)
String username, // 昵称
boolean bot, // 是否机器人
String unionOpenid, // 跨应用统一 OpenID(可能为空)
String unionUserAccount, // 跨应用统一账号(可能为空)
String userOpenid, // 用户 OpenID(单聊场景)
String memberOpenid, // 群成员 OpenID(群聊场景)
String memberRole // 群内角色:member / admin / owner
) {}author、mentions、MsgElement.author() 均为该类型。字段全部可能为 null,取值前先判空。
MessageAttachment 记录
java
public record MessageAttachment(
String url, // 附件下载 URL
String filename, // 文件名
int width, // 宽(px),非图片为 0
int height, // 高(px),非图片为 0
int size, // 字节数
String contentType, // MIME 类型
String voiceWavUrl, // 语音转 WAV 的 URL
String asrReferText // 语音 ASR 识别文本
) {}content_type 常见取值:voice(语音)、image/jpeg、image/png、image/gif、video/mp4、file(群文件)。
私聊消息事件
C2CMessageEvent
事件名:C2C_MESSAGE_CREATE
| 方法 | 返回类型 | 说明 |
|---|---|---|
getId() | String | 消息 ID |
getAuthor() | User | 发送者(单聊场景用 author().userOpenid()) |
getContent() | String | 文本内容 |
getTimestamp() | Timestamp | 事件时间 |
getMessageType() | int | 消息类型 |
getAttachments() | List<MessageAttachment> | 附件列表 |
getMessageScene() | MessageScene | 消息场景上下文(含 msg_idx) |
getArkData() | ARKData | 结构化卡片数据 |
getMsgElements() | List<MsgElement> | 消息元素列表 |
单聊回复的 openid
getAuthor() 可能为 null(服务端未下发作者信息时)。单聊场景取 userOpenid(),群场景取 memberOpenid():
java
User author = e.getAuthor();
if (author != null) {
Bngeuit.replyC2CText(author.userOpenid(), e.getId(), 1, "收到私聊");
}互动事件
InteractionEvent
用户点击按钮、快捷菜单等时触发。必须调用 Bngeuit.respondInteraction() 响应,否则客户端一直 loading:
java
import cn.org.bukkit.bngeuit.Bngeuit;
import cn.org.bukkit.bngeuit.dto.message.interaction.InteractionResponseCode;
import cn.org.bukkit.bngeuit.event.SubscribeEvent;
import cn.org.bukkit.bngeuit.event.receive.InteractionEvent;
@SubscribeEvent
public void onInteraction(InteractionEvent e) {
Bngeuit.respondInteraction(e.getId(),
InteractionResponseCode.SUCCESS);
// 按钮回调数据在 button_data
if (e.isButtonClick() && "/confirm".equals(e.getButtonData())) {
Bngeuit.sendGroupText(e.getGroupOpenid(), "已确认");
}
}常用方法:
| 方法 | 返回类型 | 说明 |
|---|---|---|
getId() | String | 互动事件 ID(用于响应) |
getType() | InteractionType | 互动类型(按钮=11、快捷菜单=12 等) |
getScene() | InteractionScene | 场景(GROUP / C2C / GUILD) |
getChatType() | ChatType | 聊天类型(群=1、私聊=2) |
getUserOpenid() | String | 操作用户 OpenID |
getGroupOpenid() | String | 群 OpenID(群场景有值) |
getButtonId() | String | 被点击按钮 ID |
getButtonData() | String | 按钮回调数据(与创建按钮时一致) |
isButtonClick() | boolean | 是否为消息按钮点击(type=11) |
isFromGroup() | boolean | 是否来自群聊 |
getTimestamp() | Timestamp | 触发时间 |
getMessageId() | String | 被操作的消息 ID |
完整方法列表
以上仅列出常用方法,完整方法列表请参阅 Javadoc 或源码 InteractionEvent.java。
好友与群管理
| 事件 | 触发时机 |
|---|---|
FriendAddEvent | 用户添加机器人为好友 |
FriendDelEvent | 用户删除机器人好友 |
GroupAddRobotEvent | 机器人被拉入群 |
GroupDelRobotEvent | 机器人被移出群 |
GroupMemberAddEvent | 新成员加入群 |
GroupMemberRemoveEvent | 成员退出群 |
GroupJoinRequestEvent | 用户申请入群 |
GroupJoinRequestEvent
用户申请入群时触发。事件名:GROUP_JOIN_REQUEST。机器人需拥有群管理员身份。
java
@SubscribeEvent
public void onJoinRequest(GroupJoinRequestEvent e) {
// 批准入群
Bngeuit.approveJoinRequest(e.getGroupOpenid(), e.getMemberOpenid(), e.getJoinRequestId());
// 或拒绝入群
Bngeuit.declineJoinRequest(e.getGroupOpenid(), e.getMemberOpenid(),
e.getJoinRequestId(), "不符合入群条件", false);
}| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 群 OpenID |
getMemberOpenid() | String | 申请人 OpenID |
getUnionOpenid() | String | 申请人跨应用统一 OpenID |
getUsername() | String | 申请人昵称 |
getJoinRequestId() | String | 申请 ID(用于传给审批 API) |
getApplyAt() | Timestamp | 申请时间 |
getApplySource() | ApplySource | 申请来源:SELF_APPLY(直接申请)/ INVITED(被邀请) |
getInvitedBy() | String | 邀请人 OpenID(仅 INVITED 时有值) |
isBot() | boolean | 申请人是否为机器人 |
getRiskTips() | String | QQ 安全系统风险提示 |
getVerifyInfo() | VerifyInfo | 入群验证信息 |
getAutoApproved() | AutoApproved | 自动审批信息(策略命中时有值) |
isAutoApproved() | boolean | 是否被自动审批策略通过 |
FriendAddEvent
用户添加机器人为好友时触发。事件名:FRIEND_ADD。author 字段不是消息事件里的 User——好友事件的 payload 只带 union_openid,因此独立为 FriendAuthor(dto.message.friend)。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getOpenid() | String | 用户 OpenID |
getTimestamp() | Timestamp | 添加时间 |
getScene() | FriendAddScene | 添加来源场景枚举 |
getSceneParam() | String | 来源场景参数 |
getAuthor() | FriendAuthor | 用户信息(unionOpenid) |
getUnionOpenid() | String | 跨应用统一 OpenID(等价 getAuthor().unionOpenid()) |
getShortCode() | String | 用户短 ID |
FriendDelEvent 携带 getOpenid()、getTimestamp()、getAuthor()(FriendAuthor)与 getUnionOpenid()(等价 getAuthor().unionOpenid())。
系统事件
- ReadyEvent — 每个分片连接鉴权完成各触发一次(仅 WebSocket 模式,网关真实 READY)
- WebhookReadyEvent — Webhook 回调服务启动完成后触发一次(仅 Webhook 模式,无伪造 ReadyEvent)
- ResumedEvent — 断线重连(RESUME)成功(仅 WebSocket 模式)
插件推荐:onActive()
做「机器人已上线」的初始化(启动定时推送、上报在线状态等)时,推荐实现 Plugin.onActive():该生命周期在两种接入模式统一触发一次(WS 全部分片就绪 / Webhook 回调服务就绪),无需关心接入方式与分片,也无需自行防重。
发送事件(可取消)
所有发送事件实现 Cancellable,在 HTTP 调用前触发,取消则操作跳过、API 返回 null。常用字段示例(以 GroupMessageSendEvent 为例):
| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 目标群 |
getMsgType() | MsgType | 消息类型 |
getContent() | String | 文本内容 |
getMarkdown() | MessageMarkdown | Markdown 负载 |
getKeyboard() | Keyboard | 内嵌键盘 |
getCard() | Card | 图文卡片 |
getMedia() | MediaInfo | 富媒体信息 |
getMsgId() | String | 被动回复的原消息 ID |
getMsgSeq() | int | 消息序号 |
getMessageReference() | MessageReference | 消息引用 |
setCancelled(boolean) | — | 取消本次操作 |
C2CStreamMessageSendEvent
私聊流式消息发送事件,字段使用枚举类型:
| 方法 | 返回类型 | 说明 |
|---|---|---|
getUserOpenid() | String | 目标用户 |
getInputMode() | InputMode | 输入模式(InputMode.APPEND / InputMode.REPLACE) |
getContentType() | StreamContentType | 内容类型(StreamContentType.TEXT / StreamContentType.MARKDOWN) |
getContentRaw() | String | 消息内容 |
getInputState() | InputState | 输入状态(InputState.GENERATING / InputState.DONE) |
getIndex() | int | 分片序号,从 0 递增 |
getEventId() | String | 被动回复事件 ID |
getMsgId() | String | 被动回复消息 ID |
getStreamMsgId() | String | 流式消息 ID(首片为空) |
getMsgSeq() | int | 消息序号 |
setCancelled(boolean) | — | 取消该分片发送 |
InputState 枚举
流式消息输入状态,用于 StreamMessageRequest 和 C2CStreamMessageSendEvent:
| 枚举 | 值 | 说明 |
|---|---|---|
GENERATING | 1 | 生成中 |
DONE | 10 | 生成结束 |
分片上传事件携带 File 对象:GroupUploadChunkedEvent.getFile() / getFileName() / getFilePath()。
GroupMemberMuteEvent
群成员禁言操作前触发。取消可阻止禁言操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 群 OpenID |
getMembers() | List<SetMemberMuteState> | 禁言设置列表(含 op、memberOpenid、muteExpireAt) |
JoinRequestApproveEvent
批准入群申请前触发。取消可阻止批准操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 群 OpenID |
getMemberOpenid() | String | 申请人 OpenID |
getJoinRequestId() | String | 申请 ID |
JoinRequestDeclineEvent
拒绝入群申请前触发。取消可阻止拒绝操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 群 OpenID |
getMemberOpenid() | String | 申请人 OpenID |
getJoinRequestId() | String | 申请 ID |
getRejectReason() | String | 拒绝理由 |
isAddToBlacklist() | boolean | 是否加入黑名单 |
UrlLinkGenerateEvent
生成分享链接前触发。取消可阻止生成操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getCallbackData() | String | 回调数据(链接被点击时携带) |
MenuUpdateEvent
修改 C2C 自定义菜单前触发。取消可阻止修改操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getMenu() | Menu | 新菜单配置 |
PanelCreateEvent
创建指令面板前触发。取消可阻止创建操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getScope() | String | 生效场景("c2c" / "group") |
getTargetType() | String | 作用范围("all" / "specific") |
getUserOpenids() | List<String> | 关联用户列表(C2C + specific 时有效) |
getGroupOpenids() | List<String> | 关联群列表(group + specific 时有效) |
getPanel() | Panel | 面板配置 |
PanelUpdateEvent
修改指令面板前触发。取消可阻止修改操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getPanelId() | String | 面板 ID |
getPanel() | Panel | 新面板配置 |
PanelDeleteEvent
删除指令面板前触发。取消可阻止删除操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getPanelId() | String | 面板 ID |
PanelTargetUpdateEvent
修改指令面板关联对象前触发。取消可阻止修改操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getPanelId() | String | 面板 ID |
getRequest() | PanelTargetRequest | 关联对象修改请求(含 op、userOpenids、groupOpenids) |
GroupMemberBatchRemoveEvent
批量移除群成员前触发。取消可阻止本次移除。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 群 OpenID |
getMemberOpenids() | List<String> | 待移除的成员 member_openid 列表 |
isAddToMemberBlacklist() | boolean | 是否同时加入群黑名单 |
GroupMemberBlacklistEvent
群黑名单操作(加入 / 移出)前触发。取消可阻止本次操作。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getGroupOpenid() | String | 群 OpenID |
getOp() | BlacklistOp | 操作类型(ADD / DEL) |
getMemberOpenids() | List<String> | 目标成员 openid 列表 |
MediaImagePrepareEvent
图片素材申请上传前触发。取消可阻止整个上传流程。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getFileName() | String | 文件名 |
getFileSize() | long | 文件大小(字节) |
getFormat() | MediaImageFormat | 图片格式(即 content_type) |
getMd5() | String | 文件 MD5 |
getSha1() | String | 文件 SHA1 |
MediaImageCompleteEvent
图片素材完成上传前触发。取消可阻止提交完成。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getUploadId() | String | 上传任务 ID |
getMd5() | String | 提交的 MD5(正常情况下等于直传返回的 ETag) |
MediaImageDeleteEvent
删除图片素材前触发。取消可阻止删除。
| 方法 | 返回类型 | 说明 |
|---|---|---|
getImageId() | String | 图片 ID |
后置事件(Notify)
28 个后置事件与发送事件一一配对,统一继承 NotifyEvent(不继承 SendEvent)。触发条件、取消语义与通用用法见 事件系统概述 的「后置事件」一节。
| 前置事件 | 后置事件 | 响应类型 |
|---|---|---|
GroupMessageSendEvent | GroupMessageSendNotifyEvent | MessageResponse |
C2CMessageSendEvent | C2CMessageSendNotifyEvent | MessageResponse |
C2CStreamMessageSendEvent | C2CStreamMessageSendNotifyEvent | StreamMessageResponse |
GroupFileSendEvent | GroupFileSendNotifyEvent | UploadResponse |
C2CFileSendEvent | C2CFileSendNotifyEvent | UploadResponse |
GroupUploadPrepareEvent | GroupUploadPrepareNotifyEvent | UploadPrepareResponse |
GroupUploadPartFinishEvent | GroupUploadPartFinishNotifyEvent | EmptyResponse |
GroupUploadChunkedEvent | GroupUploadChunkedNotifyEvent | UploadResponse |
C2CUploadPrepareEvent | C2CUploadPrepareNotifyEvent | UploadPrepareResponse |
C2CUploadPartFinishEvent | C2CUploadPartFinishNotifyEvent | EmptyResponse |
C2CUploadChunkedEvent | C2CUploadChunkedNotifyEvent | UploadResponse |
GroupMessageDeleteEvent | GroupMessageDeleteNotifyEvent | EmptyResponse |
C2CMessageDeleteEvent | C2CMessageDeleteNotifyEvent | EmptyResponse |
InteractionRespondEvent | InteractionRespondNotifyEvent | EmptyResponse |
GroupMemberMuteEvent | GroupMemberMuteNotifyEvent | EmptyResponse |
GroupMemberBatchRemoveEvent | GroupMemberBatchRemoveNotifyEvent | BatchRemoveMembersResponse |
GroupMemberBlacklistEvent | GroupMemberBlacklistNotifyEvent | BlacklistOpResponse |
JoinRequestApproveEvent | JoinRequestApproveNotifyEvent | EmptyResponse |
JoinRequestDeclineEvent | JoinRequestDeclineNotifyEvent | EmptyResponse |
UrlLinkGenerateEvent | UrlLinkGenerateNotifyEvent | UrlLinkResponse |
MenuUpdateEvent | MenuUpdateNotifyEvent | MenuUpdateResponse |
PanelCreateEvent | PanelCreateNotifyEvent | PanelCreateResponse |
PanelUpdateEvent | PanelUpdateNotifyEvent | PanelUpdateResponse |
PanelDeleteEvent | PanelDeleteNotifyEvent | EmptyResponse |
PanelTargetUpdateEvent | PanelTargetUpdateNotifyEvent | EmptyResponse |
MediaImagePrepareEvent | MediaImagePrepareNotifyEvent | MediaImagePrepareResponse |
MediaImageCompleteEvent | MediaImageCompleteNotifyEvent | MediaImageCompleteResponse |
MediaImageDeleteEvent | MediaImageDeleteNotifyEvent | EmptyResponse |
取用方式
每个后置事件都提供 getOriginal()(返回对应前置事件,带完整操作入参)与窄化后的 getResponse(),无需强转:
java
@SubscribeEvent
public void onSent(C2CMessageSendNotifyEvent e) {
C2CMessageSendEvent original = e.getOriginal();
MessageResponse resp = e.getResponse(); // 可能为 null(2xx 空体)
if (resp != null) {
log.info("已发送给 {},消息 ID={}", original.getOpenid(), resp.id());
}
}框架事件
| 事件 | 触发时机 | 说明 |
|---|---|---|
HeartbeatEvent | WS 发出心跳包(op=1)或收到心跳确认(op=11) | 无载荷、不可取消,每周期两次;Webhook 模式永不触发 |
PluginEnableEvent | 插件 onEnable 完成并登记后 | getPlugin() 取插件实例,不可取消 |
PluginDisableEvent | 插件 onDisable 与资源清理完成后 | 同上 |
这三个事件不属于 ReceiveEvent / SendEvent / NotifyEvent 体系,需按各自类型单独注册。
