示例集
约 871 字大约 3 分钟
2026-09-15
按需求挑一段抄。完整可运行版本在 plugins/demo/(demo-class.js、demo-dsl.js、render-demo.js)。
回复的几种形式
import { JiuLiPlugin } from 'jiuli'
export default class Reply extends JiuLiPlugin {
constructor() {
super({
name: '回复示例',
rule: [
{ reg: '^#纯文本$', fnc: 'text' },
{ reg: '^#引用$', fnc: 'quote' },
{ reg: '^#艾特$', fnc: 'at' },
{ reg: '^#撤回$', fnc: 'recall' },
{ reg: '^#图片$', fnc: 'image' },
],
})
}
async text(e) { return e.reply('普通消息') }
async quote(e) { return e.reply('引用你这条消息', true) }
async at(e) { return e.reply([segment.at(e.user_id), ' 你好']) }
async recall(e) { return e.reply('10 秒后撤回', true, { recallMsg: 10 }) }
async image(e) { return e.reply(segment.image('https://example.com/a.png')) }
}权限控制
super({
name: '管理',
rule: [
// 框架负责鉴权,不满足时自动回复权限提示
{ reg: '^#全员广播\s*(.+)$', fnc: 'broadcast', permission: 'master' },
],
})
async broadcast(e) {
const text = e.msg.replace(/^#全员广播\s*/, '')
// 遍历机器人加入的群
for (const [groupId] of e.bot.gl) {
await e.bot.pickGroup(groupId).sendMsg(text)
}
return e.reply('已广播')
}需要自己判断时用 e.isMaster / e.isOwner / e.isAdmin。
问答式会话
super({ name: '改名', rule: [{ reg: '^#改名$', fnc: 'start' }, { reg: '^#取消$', fnc: 'cancel' }] })
async start(e) {
this.setContext('onName') // 下一条消息会进 onName
return e.reply('请发送新名字(发送 #取消 退出)')
}
async onName(e) {
this.finish('onName')
await redis.set(`name:${e.user_id}`, e.msg.trim())
return e.reply(`已改名为 ${e.msg.trim()}`)
}
async cancel(e) {
this.finish('onName')
return e.reply('已取消')
}Promise 版本:
async start(e) {
await e.reply('请输入内容')
const next = await this.awaitContext(120)
if (!next) return e.reply('超时了')
return e.reply(`收到:${next.msg}`)
}定时推送
import { JiuLiPlugin } from 'jiuli'
export default class Daily extends JiuLiPlugin {
constructor() {
super({
name: '每日推送',
task: [{ name: '早报', cron: '0 8 * * *', fnc: 'morning' }],
})
}
async morning() {
// 定时任务没有 this.e
for (const bot of [...(Bot.bots?.values?.() ?? [])]) {
for (const [groupId] of bot.gl ?? []) {
await bot.pickGroup(groupId).sendMsg('早上好')
}
}
logger.mark('[每日推送] 已推送')
}
}渲染一张卡片
模板 plugins/my-plugin/resources/help/index.html:
<div class="card">
<h2>{{ title }}</h2>
<p>{{ time }}</p>
<ul>{{ each list item }}<li>{{ item }}</li>{{ /each }}</ul>
</div>代码:
async help() {
const img = await this.render('help/index', {
title: '帮助',
time: new Date().toLocaleString('zh-CN', { hour12: false }),
list: ['#指令一', '#指令二'],
})
return this.reply(img || '渲染失败')
}处理图片消息
super({ name: '存图', rule: [{ reg: '^#存图$', fnc: 'save' }] })
async save(e) {
const url = e.img?.[0] || (await e.getReply())?.img?.[0]
if (!url) return e.reply('请把图片和这条指令一起发,或引用一张图')
const res = await fetch(url)
const buf = Buffer.from(await res.arrayBuffer())
fs.writeFileSync(`${this.dataDir}/${Date.now()}.png`, buf)
return e.reply('已保存')
}读取自己的配置
export default class Config extends JiuLiPlugin {
constructor() {
super({ name: '配置示例', rule: [{ reg: '^#配置$', fnc: 'show' }] })
}
async init() {
const { config } = await this.useConfig({ enable: true, tips: '默认提示' })
this.config = config
}
async show(e) {
return e.reply(`enable=${this.config.enable} tips=${this.config.tips}`)
}
}旧写法(云崽一致):
import makeConfig from '../../lib/plugins/config.js'
const { config, configSave } = await makeConfig('my-plugin', { enable: true })
config.enable = false
await configSave()跨插件调用
// A 插件注册
handler('a.weather', async (e, city) => `${city} 晴`)
// B 插件调用
if (e.runtime.handler.has('a.weather')) {
const text = await e.runtime.handler.call('a.weather', e, '上海')
await e.reply(text)
}拦截某条指令(分发钩子)
import { definePlugin, command, beforeMessage } from 'jiuli'
export default definePlugin({
name: '限流',
apps: [
beforeMessage(async e => {
if (!e.isMaster && e.msg.includes('刷屏')) {
await e.reply('被限流了')
return false // 本次分发到此为止
}
}),
command(/^#正常$/, async e => e.reply('ok')),
],
})写一个适配器
Bot.adapter.push({
id: 'MyPlatform',
name: 'MyAdapter',
path: 'MyAdapter', // 唯一路径,用于去重
async load() {
const uin = '10000'
const bot = {
adapter: this, uin, nickname: '机器人', version: '1.0.0',
fl: new Map(), gl: new Map(), gml: new Map(),
stat: { start_time: Date.now() / 1000 },
pickFriend: id => contact(id),
pickGroup: id => contact(id),
pickMember: (g, u) => contact(u),
}
Bot[uin] = bot
Bot.uin.push(uin)
Bot.em(`connect.${uin}`, { self_id: uin, bot })
// 收到消息时(OneBot11 字段)
Bot.em('message.group.normal', {
post_type: 'message', message_type: 'group', sub_type: 'normal',
self_id: uin, user_id: '10001', group_id: '123456', message_id: '1',
raw_message: '#帮助',
message: [{ type: 'text', text: '#帮助' }],
sender: { user_id: '10001', nickname: '小明', card: '' },
})
},
})contact 需要提供 sendMsg / recallMsg / makeForwardMsg / getInfo 等方法。 Bot.em 会逐级派发(message.group.normal → message.group → message)并补全事件字段。 可参考 plugins/adapter/mock.js。
