事件对象与权限
约 915 字大约 3 分钟
2026-09-15
事件对象 e 的字段名与云崽完全一致,迁移时不用改。
常用字段
| 字段 | 说明 |
|---|---|
e.msg | 文本消息(多段自动拼接,已做 # / * 归一化) |
e.raw_message | 原始消息文本 |
e.message | 消息段数组 |
e.img | 图片 URL 数组 |
e.at / e.atBot | 被 at 的用户列表 / 是否 at 了机器人 |
e.user_id / e.group_id / e.self_id | 用户 / 群 / 机器人账号 |
e.post_type / e.message_type | 事件类型 |
e.isGroup / e.isPrivate | 场景 |
e.isMaster / e.isOwner / e.isAdmin | 权限 |
e.sender | { user_id, nickname, card, role } |
e.group / e.friend / e.member | 群 / 好友 / 成员对象 |
e.runtime | 运行时:render / handler / cfg / common |
群 / 好友 / 成员对象上常用:sendMsg、recallMsg、pickMember、makeForwardMsg、getInfo、getMemberMap。
权限
三档权限,写在 rule 的 permission 上;不满足时框架会直接回复提示(不会静默失败):
| 值 | 含义 |
|---|---|
master | 主人(config/config/other.yaml → master) |
owner | 群主 |
admin | 群管理员 |
代码里判断用 e.isMaster / e.isOwner / e.isAdmin。 另外框架会为每个插件生成开关(config/config/group.yaml → enable / disable)。
发送消息
await e.reply('纯文本')
await e.reply('带引用', true)
await e.reply([segment.at(e.user_id), '\n你好'])
await e.reply('10 秒后撤回', true, { recallMsg: 10 })
await e.reply('顺便 at 对方', false, { at: true })
await e.group.sendMsg('主动往群里发')
await e.friend.sendMsg('私聊发送')
await Bot.sendGroupMsg(bot_id, group_id, '指定机器人发送')e.reply 返回 { message_id },可用于后续撤回。
读取消息
const reply = await e.getReply() // 引用消息(没有则 undefined)
const url = e.img?.[0] // 图片直链
await e.recall() // 撤回这条消息
e.logText = '自定义日志里显示的内容' // 脱敏 / 简化日志会话上下文
按「插件名 + bot + 群 / 用户」隔离,默认 120 秒超时:
async start() {
this.setContext('waiting')
return this.reply('请发送内容')
}
async waiting() {
this.finish('waiting') // 结束会话
return this.reply(`收到:${this.e.msg}`)
}或者用 Promise:
async ask() {
const next = await this.awaitContext() // 超时返回 false
if (next) await this.reply(`收到:${next.msg}`)
}resolveContext() 可以主动结束正在等待的会话。
跨插件调用
// 注册(类形态)
super({ name: 'A', namespace: 'a', handler: [{ key: 'a.hello', fnc: 'hello' }] })
async hello(e, args) { return `hello ${args}` }
// 注册(DSL)
handler('a.hello', (e, args) => `hello ${args}`)
// 调用
const ret = await e.runtime.handler.call('a.hello', e, '参数')
const exists = e.runtime.handler.has('a.hello')reject() 机制与云崽一致:fn(e, args, reject),调用 reject() 表示放弃这次结果。
旁路监听原始事件(Bot.on)
Bot 本身就是 EventEmitter(JiuLiBot extends EventEmitter),全局 Bot 上 on / once / emit / off / addListener / removeListener / removeAllListeners / listenerCount / eventNames 全部可用。
// 插件顶层注册即可,能收到所有原始消息
Bot.on('message', e => {
logger.debug(`${e.group_id ?? e.user_id} 发了 ${e.raw_message}`)
})
Bot.on('notice.group.poke', e => { /* 戳一戳 */ })
Bot.on('connect.' + Bot.uin[0], ({ bot }) => logger.mark(`账号 ${bot.uin} 已连接`))事件名与云崽一致,且逐级派发:
| 事件名 | 收到什么 |
|---|---|
message | 所有消息(群 + 私聊 + 各子类型) |
message.group | 群消息 |
message.group.normal | 群普通消息 |
notice.* / request.* | 通知 / 请求事件 |
connect.<uin> / online | 账号连接 / 框架就绪 |
几个要点:
Bot.on('message')拿到的是原始事件:不经过accept/rule,也没有权限校验与冷却, 适合统计、审计、旁路记录Bot.em(name, data)逐级 emit(message.group.normal→message.group→message), 并补全bot/friend/group/member/sender——仅当data.self_id是已注册账号- 监听器按注册顺序触发;插件在
lib/events/*之前加载,所以在插件顶层注册的监听器先收到事件 - 要走权限、冷却、指令匹配的请用
accept()或rule,不要在Bot.on里自己解析文本
计数与统计
框架按云崽的键名记录消息收发,可以直接读:
Yz:count:receive:msg:total:day
Yz:count:send:msg:group:123456:month写回是内存聚合后定时批量执行的(jiuli.yaml → perf.count_flush_ms,默认 1 秒,0 为实时写)。
