#指令 与交互
小程序装到群里之后,群成员可以用 #指令 唤起它。
语法
#<command> [参数]command是 manifest 里声明的指令名(A-Za-z0-9_-,最长 24,统一小写);- 指令后面用空格隔开的就是
args,原样传给小程序; - 例:
#roll、#roll 2d6、#note 周五例会。
解析顺序
- 输入框提交时,平台先用
/^#([A-Za-z0-9_-]{1,24})(?:\s+(.*))?$/匹配整条文本; - 不匹配 → 当作普通消息发送(完全不影响正常聊天);
- 匹配 → 在当前会话可见的安装记录里找:
- 先找群级安装的同命指令;
- 再找个人级安装的同命指令;
- 找到 → 打开小程序并传参,原文不会发出去;
- 没找到 → 仍然按普通消息发送(
#开头也可能是正常的聊天内容)。
输入时的补全
输入框里敲 # 开头(且还没打空格)时,上方会浮出候选列表:
- 最多 6 条,显示图标、
#指令名、小程序名与简介; ↑/↓切换,Enter或Tab直接调用,Esc关掉;- 也可以直接用鼠标点。
候选只包含当前会话可见的安装:群级安装的本群小程序 + 你自己的个人级安装。
小程序侧:接收调用
平台在握手完成后下发一次 invoke 事件:
js
CircleChat.on('invoke', (p) => {
// p.command 'roll'
// p.args '2d6'
// p.chatId 'g:xxxx'
});注意两点:
- 事件可能早于
ready()触发,也可能晚一拍。稳妥写法是先ready(),再注册on('invoke'), 同时缓存一次事件参数(避免注册晚了漏掉这次调用): - 通过侧栏点开(不是指令唤起)时不会有
invoke事件,页面要能独立工作。
js
let pending = null;
CircleChat.on('invoke', (p) => { pending = p; run(p); });
CircleChat.ready().then((ctx) => {
if (pending) run(pending); // 事件先到了
else render(ctx); // 正常打开
});典型交互模式
A. 指令即结果(掷骰子、抽签、翻译)
拿到 invoke 后直接算出结果,有 message.send 就发到群里,让所有人看到:
js
CircleChat.on('invoke', async (p) => {
const v = 1 + Math.floor(Math.random() * 6);
await CircleChat.sendMessage(`🎲 ${p.args || '1d6'} → ${v}`);
CircleChat.close(); // 用完就关,不打扰
});B. 指令打开面板(便签、投票、统计)
唤起后停在面板里让人操作,是否发消息交给用户决定。
C. 无指令的小工具
不声明 command,只能从侧栏「小程序」分区点开。适合计算器、换算器这类纯本地工具。
边界与注意
| 情况 | 行为 |
|---|---|
| 群里没装这个小程序 | #xxx 当普通消息发出去 |
| 装了但指令名对不上 | 同上 |
私聊里用 #xxx | 会匹配你的个人级安装(群级安装只在对应群生效) |
只是想发一个 # 开头的普通文本 | 打完按 Esc 关掉候选,再回车发送 |
| 同一指令在群级和个人级都装了 | 群级优先 |
| 指令被禁言/处罚拦截 | 小程序代发消息时会返回 api.msg.muted / api.msg.banned,页面应给出提示 |
调用记录
每次成功唤起会记一条审计日志(mini.invoke,含指令名),管理员可在「日志 → 小程序」里看到谁在什么时候调用了什么。 小程序自己发消息还会另记一条 mini.msg。