词库插件 DSL 语法使用手册
v1.2.2

词库插件 DSL 语法使用手册

零基础也能上手的 QQ 机器人自动回复编写指南

开始

欢迎

欢迎!本手册将教你如何使用词库插件的 DSL(领域专用语言)来编写 QQ 机器人的自动回复功能。即使你没有编程经验,按照本手册的步骤也能轻松上手。

本手册基于 dicpro.ck 示例文件编写,建议你一边看手册一边对照 dicpro.ck 文件学习。

什么是 .ck 文件

每个 .ck 文件可以包含多条规则,每条规则定义了:当用户发送什么消息时,机器人应该做什么。

.ck 文件是词库插件的源文件。你只需要用记事本(或任何文本编辑器)创建一个以 .ck 结尾的文件,放入"词库文件"文件夹,机器人就会自动读取并生效。

文件的基本结构

一个 .ck 文件由多个"段落"组成,段落之间用空行(至少一个空行)分隔。

每个段落的结构是:

  • 第一行:触发指令(匹配用户消息的规则)
  • 后续行:要执行的代码(机器人收到匹配消息后执行的操作)

示例(最简单的你好机器人)

DSL
你好
$发送 你也好呀!$

上面这个例子表示:当有人在群里发送"你好"时,机器人会回复"你也好呀!"。

提示
一个 .ck 文件里可以写很多个段落,每段之间用空行隔开即可。

基础语法

触发指令(词条匹配规则)

触发指令写在每个段落的第一行,用来匹配用户发送的消息。

1. 精确匹配

直接写文字,表示用户必须发送完全相同的文字才会触发:

DSL
你好
$发送 hi$

用户发"你好"触发,发"你好呀"不触发。

2. 正则表达式匹配

如果你懂正则表达式,可以使用括号来捕获用户消息中的部分内容,捕获的内容可以通过 %括号1%%括号2% 等变量在后续代码中使用:

DSL
括号测试([\s\S]*)
a = %括号1%
$发送 你发送的内容是:%a%$

上面例子中,用户发送"括号测试xxxxx",xxxxx 会被捕获到 %括号1% 中。

如果需要匹配多个部分,用多个括号:

DSL
测试md([\s\S]*)#([\s\S]*)
a = $直发按钮 %括号1% %括号2%$
$发送 ±md content=%a%±$

多个参数匹配可使用正则对指定字符内容屏蔽,或使用分割标识字符进行参数分割,该分割字符需加入指令,例如:测试mc你好#我不好

正则入门
软件在设计时会强制加一层非贪婪匹配的兜底,所以无法使用贪婪的正则语法
关于正则的更多知识,可以搜索"正则表达式入门"来学习。最常用的两个:
  • ([\s\S]*) —— 匹配任意内容(包括换行)
  • (.*) —— 匹配任意内容(不包括换行)

3. 内部函数(不对外触发)

[内部] 开头的指令不会匹配用户消息,只能通过 $调用 函数名$$调用 函数名 #参数1#参数2$ 来使用:

DSL
[内部]你好
$发送 这是内部函数$

[内部]你好[参数1 参数2]
返回 参数1, 参数2

关于内部函数的详细用法,请参见"内部函数"。

变量与赋值

在 DSL 中,你可以使用变量来存储数据。变量名由字母、数字、下划线组成。

1. 赋值

使用等号 = 给变量赋值:

DSL
a = '你好'
b = 123
c = '你好世界'

赋值语法和 python 基本相同,纯数字、函数、%变量名% 和 json 可不在外部加 ""'',其他的则需要加。

如果我想在字符串里面加变量怎么办?
变量名 = f"你好我是%昵称%"
变量名2 = f"$直接发送 xxx xxx$"

2. 使用变量

使用 %变量名% 来读取变量的值:

DSL
a = '世界'
$发送 你好,%a%!$

输出结果:你好,世界!

输出消息($发送 xxx$)

$发送 xxx$ 是最常用的命令,用于让机器人发送消息到群里。

1. 基本用法

DSL
$发送 你好,欢迎你!$

2. 带变量的用法

DSL
name = 小明
$发送 你好,%name%,欢迎加入本群!$

3. 类型消息的用法

类型消息可以实现 @人、发图片、发 Markdown 等高级消息,详情见下一章。

DSL
$发送 ±at %QQ%± 你好呀!$

4. 拓展用法

发送消息到指定的群主或用户

DSL
$发送 群 群号 内容$
$发送 私 QQ 内容$

消息类型(±..±)

$发送$ 语句中,可以用 ±类型 参数± 的格式来插入特殊的消息元素。

格式:±类型 参数1=值1,参数2=值2±

新版参数简写
在新版中加入了参数名兜底解析策略,可省略参数名:
旧:±md content=你好±
新:±md 你好±

1. 组合多种类型消息

可以在一行中组合多种类型消息:

DSL
$发送 ±md #你好±±at %QQ%±±kd %kd_data%±$

2. 发送纯文本

纯文本不需要用 ± 包裹,直接写在 $发送 里面即可:

DSL
$发送 这是纯文本$

3. @某人(at)

DSL
$发送 ±at %QQ%± 你好!$

%QQ% 是发送者的 QQ 号,所以上面会 @ 发送消息的那个人。

你也可以 @ 指定的人:

DSL
$发送 ±at 123456789± 你好!$

4. 发送 Markdown 消息(md)

DSL
content = 这是标题
$发送 ±md # %content%±$

拓展用法:$发送 ±md±你好$ 表示该消息以 md 为基础进行构造。

Markdown 内容里可以使用 \n 表示换行。

支持的 Markdown 语法:请见 QQ 官方文档

5. 发送图片(img)

DSL
pic_url = $html https://example.com$
$发送 ±img %pic_url%±$

图片可以是网络链接,也可以是本地文件路径。如果是使用在官机的md语法中组合不会生效,暂时不计划对网络路径支持,请使用QQ官机的md语法

6. 发送键盘/按钮(kd)

键盘是一种特殊的数据格式(JSON),用于在 QQ 官方机器人中显示按钮。

JSON
kd_data = {
  "rows": [
    {
      "buttons": [
        {
          "id": "btn_1",
          "render_data": {
            "label": "点我",
            "visited_label": "已点",
            "style": 1
          },
          "action": {
            "type": 1,
            "data": "777888",
            "permission": {"type": 2},
            "reply": true,
            "enter": true
          }
        }
      ]
    }
  ]
}
$发送 ±kd %kd_data%±

关于键盘格式的更多细节,请参考 QQ 官方机器人文档。或者也可以使用软件自带的按钮编辑器。

7. 语音消息

DSL
$发送 ±ptt 本地路径/网络连接±$

具体支持的语音格式文件有 silk、mp3、ogg,还有一个忘了。

条件判断(如果 / 另如果 / 否则)

条件判断用于根据不同的情况执行不同的操作。暂时未支持and并列判断语法,会在未来版本中支持

1. 基本语法

DSL
如果 变量 比较符 值
条件成立时执行
如果尾
另如果 变量 比较符 值
上一个条件不成立,但这个成立时执行
如果尾
否则
以上条件都不成立时执行

2. 支持的比较符

比较符含义
==等于
!=不等于
>大于
<小于
>=大于等于
<=小于等于

3. 完整示例

DSL
cs = '你好'
如果 cs == '你好'
$发送 你也好!$
如果尾
另如果 cs == '再见'
$发送 拜拜!$
如果尾
否则
$发送 我不太明白你的意思$
注意
  • 如果尾 表示当前 if 块的结束(在不继承比较条件的情况下必须写!)

循环(循环...循环尾)

循环用于重复执行一段代码,直到条件不再满足。

1. 语法

DSL
循环(条件)
要重复执行的代码
循环尾

2. 示例:发送多次消息

DSL
i = 0
循环(%i% < 3)
$发送 这是第 %i% 次$
i = %i% + 1
循环尾

上面的代码会发送 3 次消息(i 从 0 到 2)。

提示
循环条件中的变量可以不需要用 %变量名% 来引用,直接填入变量名即可。例如:
DSL
i = 0
循环(i < 3)
$发送 这是第 %i% 次$
i = %i% + 1
循环尾

3. 无限循环(慎用)

DSL
循环True
$发送 我会一直发!$
循环尾
警告
无限循环会导致机器人不停发消息,请谨慎使用!如果你需要在某处断循环,但未达到设置的循环自动结束的条件时,可在断处再起一行写个 break

数学计算($计算)

$计算 用于执行数学运算。计算表达式用方括号 [] 包裹。

1. 基本用法

DSL
result = $计算 [1 + 2]$
$发送 结果是 %result%$

2. 支持的运算

  • + 加法
  • - 减法
  • * 乘法
  • / 除法

3. 使用括号

重要:在 $计算 中,所有的括号都要用方括号 [] 代替圆括号 ()

DSL
a1 = 2
a2 = 8
a3 = 10
a = $计算 [a3 * [a1 + a2 * a3]]$

上面的表达式相当于数学中的:10 * (2 + 8 * 10)

3. 变量参与计算

在计算中可以直接使用变量名(不需要 % 号):

DSL
x = 5
y = 3
z = $计算 [x * y + 10]$
$发送 z = %z%$

文件读写($读文件 / $写文件)

词库插件提供了简单的文件存储功能,在授权存储权限后,数据保存在 手机目录(一般为 emulated/0/....)/cikuapk/data 文件夹中。

当你传入路径时,一般默认为 /storage/emulated/0/cikuapk/data/ 路径参数下,即当你文件在该路径下时,/storage/emulated/0/cikuapk/data/user.txt 只需传入 user.txt 即可。

当然你也可以使用绝对路径 /storage/emulated/0/其他...,即传入 /storage/emulated/0/其他文件夹/user.txt

1. 写入文件($写文件)

格式:$写 文件路径 键 值$

DSL
$写 用户数据 aaa %QQ%$

上面代码的含义:在"用户数据"文件中,以aaa为键,写入的值为用户的QQ号

文件会保存在 data/用户数据 这个路径下。文件内部格式是"键 = 值",每行一条。

不带键名的写法(直接覆盖整个文件):

DSL
$写 状态信息 在线$

2. 读取文件($读文件)

格式:$读 文件路径 键 默认值$

DSL
data = $读 用户数据 %QQ% 无$
$发送 你的数据是:%data%$

如果文件或键不存在,则返回你指定的默认值(此例中为"无")。

不带键名的写法(读取整个文件内容):

DSL
content = $读 状态信息 默认状态$

参数顺序说明:

  • 第一个参数:文件路径(相对于 data 文件夹)
  • 第二个参数:键名(可选,不写则读取整个文件)
  • 第三个参数:默认值

文件转json

文件转json

本函数只适用于本软件使用的数据存储格式,即 xxx=xxxxxxx

如何使用:res = $转json 路径$

返回示例——文件:

TEXT
张三=1
李四=9
王五=6
六群=4
流萤=10

返回:

JSON
[{'张三': 1}, {'李四': 9}, {'王五': 6}, {'六群': 4}, {'流萤': 10}]

网络请求($访问)

$访问 用于从互联网获取数据(发送 HTTP 请求)。

1. GET 请求

DSL
res = $访问 http://example.com/api/data get$
$发送 %res%$

2. POST 请求

DSL
res = $访问 http://example.com/api/data post$

3. 完整参数

DSL
res = $访问 http://example.com/api/data post headers数据 json数据$

参数依次为:网址、请求方法、请求头、请求体。

大多数情况下,你只需要用到前两个参数。

JSON 数据提取(@%变量%#路径)

$访问 返回 JSON 数据后,可以用 @%变量%#路径 来提取其中的字段。

1. 语法

DSL
@%变量名%#字段1#字段2#字段3...

# 号逐级深入 JSON 对象的层级。

2. 示例

假设 $访问 返回的数据是:

JSON
{
  "name": "小明",
  "info": {
    "age": 18,
    "city": "北京"
  }
}

提取数据:

DSL
res = $访问 http://example.com/api/user get$
name = @%res%#name
city = @%res%#info#city
$发送 %name% 住在 %city%$

name 的值是"小明",city 的值是"北京"。

内部函数($调用 / [内部])

内部函数就像是可以重复使用的"代码积木"。定义一个函数后,可以在多处调用它。

1. 定义无参数的内部函数

DSL
[内部]打招呼
$发送 你好呀,很高兴见到你!$
注意
[内部] 开头的段落不会匹配用户消息,只能被调用。

2. 调用无参数函数

DSL
$调用 打招呼$

当用户触发某个词条时,可以通过 $调用 来执行内部函数。

3. 定义带参数的内部函数

DSL
[内部]计算加法[数字1 数字2]
返回 数字1, 数字2

参数写在函数名后面的方括号中,用空格分隔。

4. 调用带参数的函数

DSL
$调用 计算加法 #5#10$

参数通过 # 号传递,每个 # 后面跟一个参数值。

有等号的参数表示关键字参数:

DSL
$调用 你好 #测试#参数2=长沙市$

第一个参数是"测试"(位置参数),第二个参数 参数2 的值是"长沙市"(关键字参数)。

5. 返回值(返回)

在内部函数中,可以使用 返回 语句将结果传回给调用者:

DSL
[内部]加法[数字1 数字2]
result = $计算 [数字1 + 数字2]$
返回 result

调用:$调用 加法 #5#10$

关于接收返回值的用法和python一致,参数1,参数2,参数n... = $调用....$

6. 完整调用测试示例

调用测试词条:

DSL
调用测试
$调用 你好$
$调用 你好 #测试#参数2=长沙市$

被调用的内部函数:

DSL
[内部]你好
a = "你好世界"
$发送 ±md #调用测试%a%±$

HTML 渲染($html)

$html 可以将网页或 HTML 代码渲染成图片。

提示
为了正确渲染在线网页的最后帧采用了特殊的渲染方案,该方案在触发渲染时,手机会有渲染界面的闪屏,时间不长,一般小于 1 秒。

1. 基本用法

DSL
a = $html 网页地址$
$发送 ±img %a%±$

不带宽高参数时,默认渲染为 800x600 的图片。

2. 指定宽高

DSL
a = $html 网页地址 1080 1080$
$发送 ±img %a%±$

3. 支持的输入类型

  • 网络地址(如 https://example.com)
  • 本地 HTML 文件路径(如 /网页/文件.html)
  • HTML 代码文本(如 <h1>你好</h1>
  • 纯文本(如"你好呀",会渲染为白底黑字)

4. 完整示例

DSL
网页图测试(.*)
a = $html %括号1% 1080 1080$
$发送 ±img %a%±$

当用户发送"网页图测试https://example.com"时,机器人会将网页渲染成 1080x1080 的图片并发送。

直发按钮($直发按钮)

直发按钮是一种特殊的 Markdown 链接,用户点击后会直接在输入框填入指定内容并自动发送。这个功能是利用了QQ官机的md语法漏洞,随时失效且仅移动端能正常生效.

1. 语法

DSL
$直发按钮 显示的文字 点击后填入的内容$

2. 示例

DSL
a = $直发按钮 点我查看天气 天气查询$
$发送 ±md %a%±$

效果:在 Markdown 消息中显示"点我查看天气"按钮,用户点击后输入框会自动填入并发送"天气查询"。

3. 配合正则捕获使用

DSL
测试md([\s\S]*)#([\s\S]*)
a = $直发按钮 %括号1% %括号2%$
$发送 ±md %a%±$

用户发送"测试md点我#天气查询",机器人会回复一个按钮。

流式回复($流式)

流式回复是 QQ 官机的私聊专属接口,主要服务于用户的 agent 对接,所以如果不在旧版开放平台配置私聊沙箱的话,只有机器人的实名主的 QQ 号能正常触发这个接口。

1. 语法

DSL
$流式 结束 替换 序号 stream_id 内容$

2. 示例

DSL
r1 = $流式 ±md #正在生成回答±$
sid = @%r1%#id
await asyncio.sleep(1)  模拟的aiagent工作耗时
r2 = $流式 1 %sid% ...已过半$
await asyncio.sleep(1)
r3 = $流式 结束 2 %sid% ..最终回答$

3. 参数说明

第一次请求默认序号为 0,后续则需要序号 +1。首次请求会返回一个 json,json 里面的 id 字段就是我们要的 stream_id

一个指令事件只能创建一个流式集合体消息,无法创建不同的流式消息集合体多次回复同一个指令。

特别说明替换参数:在 QQ 用户端的观感上替换和默认不填(官方文档里面为 append,即添加)没啥区别,但在代码这里就有区别。上方例子则需要变成这样:

DSL
r1 = $流式 ±md #正在生成回答±$
sid = @%r1%#id
await asyncio.sleep(1)  模拟的aiagent工作耗时
r2 = $流式 替换 1 %sid% #正在生成回答...已过半$
await asyncio.sleep(1)
r3 = $流式 结束 2 %sid% #正在生成回答...已过半..最终回答$

就显得十分的没必要,跟个脱裤子放屁一样。

系统事件

1. 事件

[内部]的写法一样,但目前对事件类型,即通用事件名称支持不全,所以建议使用全量事件的写法:

DSL
[事件]
%event%

使用logger.debug(event)在日志中查看该事件的json数据

DSL
[事件]
logger.debug(event)

bot实例

1. bot实例

获取成功连接的所有 bot 实例。

获取 bot 实例列表:bot_list = $BOT$

获取指定 bot 的实例:bot = $BOT appid/BOTQQ号$

如何使用:

DSL
测试日志
bot_list = $BOT$
bot1 = @%bot_list%#0#type
bot2 = @%bot_list%#1#type
bot1_u = @%bot_list%#0#bot
如果 bot1 == "ONEBOT_V11" and bot != bot1_u
bot = @%bot_list%#0#bot
$发送 群 907202438 7788$
注意
值得注意的是,当 bot 这个变量名赋值了 bot 实例,后面的代码就默认使用这个 bot 进行互动,单指令独立,如果需要换 bot 实例在代码调用前重新赋值就行了。例如:
DSL
bot = @%bot_list%#0#bot
$发送 群 907202438 7788$
bot = @%bot_list%#1#bot
$发送 群 907202438 7788$

排序

排序

支持的数据结构:

  • 纯列表:[1,2,348,77,894]
  • 多同键字典:[{"name": "Alice", "score": 85}, {"name": "Bob", "score": 92}, ...]
  • 异键字典:[{'张三': 1}, {'李四': 9}, ...]
  • 嵌套字典:[{"user": {"name": "Tom", "age": 30}}, ...]

使用示例:

DSL
$排序 %data1% 升序 None user.age \n {index}. 【{user.name}】 : {user.age}$

参数依次为:数据、排序方式(升序/降序)、取结果前几个、排序依据键、结果分割符、自定义结果。

其中分割符和自定义结果不填,即 $排序 %data1% 升序 None user.age$ 返回为原结构字典。

自定义结果中 index 代表序号,其他则为获取指定键的值。若传入的数据为异键字典,使用 keyvalue 作为占位符代表键值。item 为基本元素。

排序键依据用于多同键字典和嵌套字典。

若无复杂字典任务,即纯列表、异键字典,$排序 数据 序 几个$ 即可。

特殊变量一览

以下特殊变量可以在代码中直接使用(用 %变量名% 引用):

变量名含义
%QQ%发送消息的用户的 QQ 号
%群号%当前群聊的群号
%昵称%发送消息的用户在本群的昵称(群名片)
%括号1%正则表达式中第 1 个括号捕获的内容
%括号2%正则表达式中第 2 个括号捕获的内容
%括号N%以此类推,N 是括号的序号
%时间+表达式%获取当前时间(%时间% 或 %时间%Y-%m-%d %H:%M:%S%) 无表达式则为整数时间戳
%bot%bot实例,为系统预留变量,不可随意赋值
%event%事件数据,为系统预留变量,不可随意赋值
%regex_group%正则匹配组,为系统预留变量,不可随意赋值
%变量名%你自定义的任意变量

常见问题与注意事项

1. 多行文本赋值

当你需要给变量赋多行文本时,使用 Python 的三引号语法:

DSL
a = """第一行
第二行
第三行"""

附录

A附录A:类型速查表

±...± 支持的消息类型一览:

类型写法说明
at±at QQ号±@某个人
md±md Markdown内容±发送 Markdown 消息
img±img 图片地址±发送图片
kd±kd 键盘JSON±发送键盘/按钮
ptt±ptt 语音文件地址±发送语音消息
注意
协议不支持的消息类型均会转为 text。