欢迎来到Dice! 文档

炼骰手册

DiceMaid CookBook
骰娘是怎样炼成的

Dice!常见问题手册

前言

本手册面向Dice!V2.4.0以上的用户以及对应机器人的使用者,用于解答使用、搭建、维护、开发方面的各类问题,作为Master Manual和User Manual的补充。相对于两份手册与版本同步,本手册更多地提供纲领性说明。如有疏漏,欢迎提出。可通过向骰娘.send [发送消息内容] 发送信息。

对骰娘有了解的用户可以跳过第一部分,不自建骰娘的可以忽略第四、第五部分。本手册内容并非必须记住,但希望能在需要查阅时像开卷考试一样熟练。

安研色&溯洄
QQ:3258528467

一、骰娘是什么

骰娘是QQ账号、功能核心(掷骰)、非核心功能、(虚拟形象)相统一的有机体。一位骰娘是一个能响应掷骰功能指令的QQ(机器人),处理来自QQ的消息并通过QQ发送回复。本手册所述骰娘一般为泛指,部分语境下描述的是Dice!插件专有的骰娘特性。

为什么需要骰娘?

QQ线上玩家玩线下游戏所需要的实体骰子的替代,即“玩家共同见证的随机数生成器”,是骰娘的功能核心。当玩家需要使用功能时,发送特定格式的消息,骰娘即会响应并回复结果。

KP: 你骰一个1D100

PL: .r 1d100

DiceMaid: PC掷骰: D100=100

为什么骰娘为TRPG(跑团)服务?

开门见山地说,Dice!是一款TRPG掷骰服务插件,针对角色扮演游戏和特定游戏规则(COC/DND/WOD)特化了高级功能,简化了部分游戏内琐碎重复的操作(记录属性、理智检定、成长检定、奖励惩罚骰、计算成功等级、加骰等),帮助玩家专注于扮演体验,同时降低跑团记录整理的工作量。

当然在理论上,骰娘可以服务于所有需要实体骰子的场合。如果哪天你玩祸不单行/飞行棋/小黑屋/教改风云把骰子玩丢了,你一样可以使用骰娘来进行游戏。但对线下玩家而言,骰娘对实体骰子没有明显优势,因此在线上玩家不方便使用实体骰子的场合,骰娘才有其优越性。

相应地,文字类游戏本身适合在QQ上进行,而桌上角色扮演游戏对地图/棋盘和指示物依赖低,操作更偏向于有随机性的文游,骰娘由此与QQ跑团相辅相成。实际上你也可以用骰娘在QQ上开一局飞行棋,但如果可以,为什么不直接在平台上玩飞行棋呢?

为什么是骰“娘”?

“骰娘”是一个习称,因为元祖级公共掷骰机器人均为女性形象。现在“骰娘”的形象可以是骰郎、伪娘、AI、宠物、小精灵、神话生物、SCP收容物……事实上,一个掷骰机器人甚至可以没有任何形象设定。直至今天,绝大多数的主人仍然希望见到女性形象的骰子来服侍广大玩家。因此本手册沿用这一称呼,以指代任何形象的掷骰机器人。

什么是骰娘的主人/骰主?

主人是对骰娘有所有权、控制权的人,拥有插件内最高管理权限。这里认为主人持有骰娘QQ账号,控制骰娘酷Q所在电脑/服务器,即使实际上可能并非由同一个人负责。在Dice!较新版本中,获取和调用主人权限的指令为.master,因此主人习称为Master。

成为一名骰主并没有太多要求,因为酷Q air/Mirai是免费软件,Dice!插件免费且开源,因此顺着步骤操作很容易拥有一只属于自己的骰娘。如果实在不想自己动手,在QQ群开出百元一年全托管不含人设的价位,会有许多人乐意效劳的。目前为止,已知作为Dice!系列骰娘主人的QQ用户有至少1000位以上。

什么是公骰?

与仅供自己或亲友使用的骰娘相对,公骰是开放公开服务,可以免费供他人使用的骰娘。客人基本可以像邀请其他人一样邀请骰娘进群,而没有额外的申请步骤。一般而言,公骰也会相应地有更严格的规定,以保证对其他用户提供稳定的服务。公骰主人会花更多精力维护骰娘的运行,但也因暴露而有更高风险遭遇不可抗力的制裁。(仅供参考,公骰Master可能有不同的政策)

请务必用对待女朋友的方式对待骰娘!

——溯洄《Dice! 掷骰机器人搭建说明》(这句话其实不是我说的——溯洄注)
公骰-官骰-私骰

(某系)官骰一般指掷骰插件开发者自主运营的骰娘,如Kokona是溯洄系(Dice!)的官骰,It's DiceBot是风羽系(CatDice)的官骰,奈梅斯/坂本酱是塔(斯塔尼亚)系的官骰,惠惠是惠系的官骰,『汪酱』是汪酱系列的官骰。因此在某些语境下,私骰指代的是非官方搭建运营的骰娘(私人所有),如TRPG私骰分享站指的就是个人搭建的骰娘。但在另一些语境下,私骰指代的是不提供公共服务、无主人许可不让碰的骰娘(私人使用),对Dice!而言即开启私用模式的骰娘。一般而言“私骰”指代后者即私用骰娘

Dice!是什么?

Dice! 是一款开源免费,基于酷Q 或者Mirai 的QQ掷骰机器人,由溯洄使用C++语言编写,主要由Visual Studio编译(现在也可以用GCC/Clang等)

二、骰娘怎么了

骰娘怎么被冻结了?

主人们为了维护骰娘账号往往需要与QQ安全中心打交道,尤其是解除保护模式-QQ安全中心骰娘是典型的QQ机器人,轻易加入群聊,秒过陌生好友,以反人类的速度和频率同时在各个群发送消息,故本身有较高的封禁概率,而踢出群聊、大量刷屏等操作则会推动骰娘的暴露。因此,即使在日常运行时,骰娘也有可能被QQ安全中心检测识别为传播不良信息并封禁。而如果对骰娘进行举报,则会有较高概率被QQ所通过。因此,骰娘和用户都需要对骰娘突然被冻结做好准备。

用户在遭遇骰娘冻结的情况时,若相识可第一时间提醒骰娘主人解冻。但有时保持冻结也是一种保护,因为经验表明安全中心会对近期被冻结的骰娘进行复查,急于恢复使用有更高概率令骰娘遭遇反复冻结。

骰娘怎么拉黑我了?【版本限定】

“拉黑”是对向特定用户或群停止服务的习称。骰娘在对特定用户停止服务后,会停止响应指令,清查与用户的共同群并视情况退群,默认会向用户发送拉黑通知(主人可以在个性化时取消通知)。拉黑引出了骰娘和用户群间的孤立与反孤立,就像学生时代的校园故事。但是仅此而已,骰娘不会对被拉黑的用户做出更多实质性惩罚,或者说,不能施加更多暴力。

Dice!内置三类自动拉黑机制:移出群、群禁言、单人刷屏,前两项拉黑所在群和操作者,后一项拉黑操作者。同时骰娘会生成warning,其他骰娘在接收到消息并验证可信后会同样做拉黑处理。(注:在Dice!远古版本,骰娘缺少身份检测,会拉黑主人甚至自己。现在这种情况已经被排除了,骰娘会乖乖被主人口球。)

内置拉黑机制只是对用户协议通用条款的最基本实现协议的最终解释权归主人所有,主人完全可以手动停止对违反协议的特定用户或群的服务,特别是缺少自动判定条件的行为,如教唆犯、连带责任、刷屏、敏感词等,Dice!的开发者对一切拉黑等现象不负任何责任。

如何恢复服务?

默认协议不包含恢复服务相关规定,因此唯一的途径是让事主同意撤销黑名单,即完全取决于主人意愿(所以去官方群是没有用的)。

参考流程(不同骰主并不通用):

  • 联系事主(指主人,不要指望找骰娘有回应),表达解黑意向
  • 还原事发情形,表达态度
  • 验证自己熟悉指令用法和用户协议,保证以后遵守协议
  • 获得谅解并等待广播决议
  • _images/demo_rmban.png解黑通知
云黑是什么?

“云黑”是对骰娘间不良记录共享机制的习称。Dice!没有全体骰娘同步的黑名单,只有供核验的不良记录库和骰娘对!warning指令的响应机制。骰娘在遭遇不良行为时会自动上传记录信息,其他骰娘在接收到warning指令后与云端通讯核验信息,确认无误后同步拉黑。骰娘的任何本地黑名单记录都最终受控于骰娘管理者,骰娘永远拉黑不了自己的主人。(如果有,那就用.master指令解除。)

刷屏监测是什么?

骰娘会分别记录每个用户一段时间内的指令次数并折算指令频度,当某用户的频度达到不同阈值时,骰娘会分别初次警告、最终警告、拉黑用户。三个阈值相当于6次/30s、12次/30s、20次/30s。注意刷屏的解释权归主人所有,不要试探算法的底线,即算法不判定刷屏≠主人不认为刷屏_images/demo_spam.png刷屏警告

触发警报后,一般需要等待至少半分钟至一分钟再使用指令,否则容易短时间多次触发。例:某次结算成长累积频度到6,触发警报。这时停止指令,频度会从6逐渐降低至0。但如果在频度降到5时便等不及继续指令,频度重新达到6,再次触发警报。

解散群/移出讨论组算踢出吗?

骰娘不会意识到发生了什么,也不会触发拉黑。解散/移出之前dismiss能为骰娘管理群聊提供少许的方便。当然,骰主如果用PCQQ登录骰娘时收到了被移出讨论组的群聊系统消息,根据协议也是可以手动拉黑的。(仅供参考,不同master可能有不同政策)

协议里没说不许这样啊?

这类问题只能向主人申诉争论。可以在不影响无关群氛围的前提下提出自己的意见。

骰娘的规矩好麻烦怎么办?

参考第四部分,做自己骰娘的主人,这样就不用担心违反协议了——骰娘会永远信任自己的主人,无论被做什么都不会反抗。当然,找到一个信任自己的骰主,获得豁免级别的信任,就可以随便使用对方的骰娘了。

骰娘怎么没反应了?

首先从头像查看骰娘在线状态,确认骰娘是否在线,不在线自然不会响应。之后分情况讨论:

私聊有回应,群内不响应

请检查日志,如果日志中有发送消息的记录证明你的消息被腾讯当作垃圾消息/广告等内 容拦截,无法正常发送。这一现象在新账号身上发生很频繁。请尝试正常使用此账号几天或 更换账号重试。此问题并非插件问题,也没有确切的解决方法。_images/faq_group_no_ans.jpg群聊被屏蔽

群内有回应,私聊不响应/暗骰不私聊

当骰娘与用户不是好友、私聊需要通过群聊窗口发起时,发送有概率发生错误:找不到与对象的关系。通过添加好友来修复这一错误。

装载其他应用后,骰娘不工作了怎么办?

请打开酷Q的应用面板,检查优先级,确保没有更高优先级插件拦截指令(数字越小,优先级越高,但只在10001到40000之间合法)。

三、骰娘怎么用

善于查表

用户要善于查指令(.help指令,或查看用户手册),骰主要善于查回执条目表、全局设置表(及时更新版本适配的Master手册,使用测试版本的需要适配测试手册)以上手册下载地址可以在官方网站 找到。

指令怎么用?

img

指令是能触发骰娘响应的消息。所有的掷骰指令都以前缀标识符(.。!!之一)、指令名("r")、参数("d")构成。前缀标识符帮助骰娘快速过滤一般聊天消息,指令配合参数表达想要实现的功能。部分指令可忽略参数。请养成参数之间带空格的好习惯!完整指令说明请熟读用户手册,必要时对骰娘发送.help 指令,注意说明中方括号表示转义,圆括号表示可省略,不要单纯替换而保留括号。

注:在Dice!2.3.8exp10之后,半角方括号[]会在录入角色卡表达式时用到

指令速查
复数骰娘时怎么指名?

可以想象,存在一个群内同时存在多位骰娘的情况,而正常使用只需要用到一位骰娘,而其他骰娘则被希望不响应指令。此时,可以at指定骰娘并接指令,令指定骰娘响应;也可以停用其他骰娘的指令。当我们需要停用骰娘DiceMaid(123456789)的指令响应时,以下指令是等效的:

@DiceMaid .bot off
.bot off 6789
.bot off 123456789

类似地可以通过.bot on重新启用指令。

今日人品高好还是低好?

Dice!2.3.6之后,今日人品在1-100的区间内均匀分布,解读没有规定。实际以骰娘文本描述为准。

_images/demo_strJrrp.png今日人品

怎么邀请骰娘?

请确认熟悉骰娘指令!请确认同意骰娘协议!请确认邀请骰娘得到群内同意! 默认协议下默认邀请前已完成确认。如果自己不是群管理或得到群主同意,请不要擅自邀请骰娘,因为你不能对骰娘负责,这同样也是对骰娘所进的群不负责任。

认识要用的骰娘

QQ签名、说说动态、help文档都是骰娘展示自己的空间。为你和骰娘将来的体验负责,在使用前请尽可能了解你选中的骰娘。

一定要给骰娘管理吗?

Dice!较新版本中,给予骰娘群权限的群能得到更多信任,这暗示群主授予骰娘管理员权限。然而,由于对骰娘全部功能的未知,这种轻率地授予信任可能反过来带来风险,如擅自禁言或踢出群员。现在有越来越丰富的方式建立骰娘与群之间的互信,因此我们同更推荐群主规训群管理并申请群白名单,特别是在骰娘多且无法同时当管理时。(以上仅供参考)

可以收集骰娘吗?

没有强制约束。但注意主人是知道骰娘被谁加好友、被拉进了什么群的。

骰娘可以见一个加一个好友吗?

没有强制约束,但请克制:骰娘是用完好友位也加不过来的;频繁加好友会被QQ临时限制好友功能;占用骰娘好友位又放置不管,这会冒犯到一部分骰主。

可以拉群把骰娘都拐进来吗?

请慎重:对部分骰娘而言,邀请进无关群本身违反协议;拉骰娘越随意,不良行为的风险越高;骰娘会在自动清理时自行离开。

可以调戏骰娘吗?

以下内容仅供参考,不同master可能对此问题由完全不同的想法,推荐先征求对方同意

骰娘的态度取决于主人对骰娘的定位。如果不清楚或没把握,请放弃这个想法。想象对一个现实的陌生人说出你要说的话会是什么样子。

工具型骰娘:骰娘是工具人。调戏=调戏一个没有感情的女仆

替身型骰娘/人机一体:骰娘是主人的假面/形象与主人统一。调戏=调戏骰主

扮演型骰娘(绊爱型):骰娘是有独立人设的皮套,扮演者服务于形象。调戏=调戏骰娘的人设形象

养成型骰娘:骰娘是主人的梦中人具现化的对象。调戏=调戏主人的伴侣/情人/儿女

骰娘身上每一处萌点,都来自主人藏在骨子里的可爱。

——安研色《自己现说的》
可以私聊骰娘吗?

尽量避免发送不当内容。(Dice!官方无法保证任何机器人的安全性,无法排除对方恶意利用聊天内容的可能,对于任何机器人都请谨慎对待,请不要发送包括但不限于隐私信息,密码,信用卡号等,在有骰娘的群也是如此)

可以金钱援助解锁交际功能吗?

欢迎用户为骰娘提供QQ会员开通、酷Q pro续费、服务器续费等援助。至于骰娘的回报……说不定骰运会好点哦。

可以拿骰娘当作文件中转/记录吗?

公共骰娘偶尔会收到没头没尾的扮演文本、模组内容、人物卡和奇奇怪怪的文件,因为会有人把这当作存储临时内容的窗口。请尽量使用QQ自带的文件助手替代,因为一旦发粉红内容给骰娘,骰娘还接收了的话,事情就比较尴尬了。(我就收到过——溯洄)

四、骰娘怎么做

简而言之:准备一个干净的QQ号,下载一份酷Q 或者Mirai,装入Dice插件,用QQ账号密码登录酷Q(可能需要登录验证),在应用管理内启用应用后,一只新骰娘就诞生了。

整合包可以在官方网站 找到下载地址

什么是酷Q【酷Q】?

酷Q是一款QQ机器人软件。简单理解,酷Q利用QQ协议假冒成设备上的QQ进行登录,调用QQ的接口,接收QQ的事件。正常QQ会把接收的消息放到交互窗口供用户响应,酷Q则会调用库中插件进行处理。

酷Q是一款智能机器人软件,严禁将酷Q用于违法用途(如发送广告/群发/诈骗、色情、政治等内容)。

酷Q是一款智能机器人软件,由应用提供群管理、智能对话、好友互动、生活助理等智能功能。

您不得利用本软件从事赌博(《刑法》第三百零三条)、传播色情(《刑法》第三百六十四条)、诈骗(《刑法》第二百六十六条)等违法行为。

​ ——酷Q官网

酷Q使用易语言编写,只提供运行在 Windows 平台的版本,在 Mac 或 Linux 平台需要自行搭建 wine 环境或使用 Docker 映像。 _images/intro_cq_dir.png日志定时清理

酷Q做不到什么【酷Q】?

(酷Q自带的接口监测不到或处理不了)拉取讨论组成员列表、监控讨论组移出、监控群解散、监控消息撤回(被撤回的消息记录不会删除)

酷Q不会处理离线时QQ收到的消息。因此离线时被禁言、踢出无法追溯

为什么同一个酷Q文件夹不能多开【酷Q】?

电脑端只需要安装一个PC版QQ就能多开,因为每个登录QQ账户的相关数据有各自的文件夹,不会冲突。而酷Q配置文件和数据存档的相对路径都是固定的 (除了日志存放路径) ,因此想要多开多少个骰娘,就得准备多少酷Q文件夹。建议在下载酷Q后留一份备份,以免要多开时重新下载。

怎么安装插件/认主?

对于酷Q: 将插件cpk放入准备好的酷Q文件夹的app文件夹下,重载应用。

_images/demo_cq_app.pngapp文件夹

_images/demo_app_manager.png开启应用

对于Mirai: 安装Mirai-Native, 将插件的dll和json放入plugins/MiraiNative/plugins文件夹,重载应用

开启Master模式有两种方法:

  1. 点击菜单的M综合管理-Master设置,在输入框内输入MasterQQ,点击设置。Mirai用户请右键菜单栏内的流泪猫猫头。
  2. 点击菜单的Master模式切换,开启后第一个对骰娘.master (public)的用户将获得骰娘的唯一Master权限。Mirai用户请右键菜单栏内的流泪猫猫头。

公骰认主私骰认主

认主都有什么用?

监控骰娘活动,指令自定义骰娘文本,调节骰娘的好友申请、群邀请等全局设置,远程控制骰娘言行。刷屏、禁言、踢自己的骰娘不会被处分。

应用菜单在哪儿?

右键点击悬浮窗->【应用】->【应用管理】->【菜单】 或 右键点击悬浮窗->【应用】->【Dice!】->[菜单选项]。 Mirai用户请右键菜单栏内的流泪猫猫头。

应用菜单
悬浮窗显示的是什么?

悬浮窗默认显示每分钟结束应用处理的消息数,一般不超过千条

如何维持骰娘运行?

保证开机!/关机后骰娘就没反应了么?

想象一个离线的QQ一直对你发消息? 骰娘的事件和消息是由酷Q的进程处理的,骰娘保持运行状态要求酷Q保持运行状态,且网络正常。运行骰娘需要一直保持软件开启,因此除非短时间使用,推荐使用云服务器。由于酷Q Air使用安卓端QQ协议,因此不能和手机QQ同时使用 (包含Android QQ, iOS QQ 以及轻聊版),推荐使用小号登录。酷Q Pro和Mirai可以使用平板协议,这样就不会产生冲突了。

保护骰娘的QQ账号

QQ是骰娘的载体,一个QQ被永久冻结的骰娘除了换号转生,只有毕业的结局。因此,维护骰娘的首要任务,是保证骰娘QQ免受制裁(屏蔽消息、屏蔽空间、冻结封禁等)。而骰娘作为一类QQ机器人,在服务的过程中必然承担被制裁的风险。

为了降低封禁风险,请避免骰娘频繁被踢出群或大量发送消息。善用黑名单功能,主人有权利自行修改骰娘的用户协议,必要时完全可以自行.admin blackqq [用户qq] 拉黑[用户qq]

新骰娘号怎么养?

新注册或长期未用的QQ账号不宜立即作为骰娘投入使用,建议先作为正常用户使用、聊天。尽可能降低异地/陌生ip、陌生设备登录情形下的暴露风险。

定期清理文件

仅限酷Q, Mirai不会记录日志

骰娘的日志记录和缓存图片会随着运行时长不断积累,并逐渐挤占存储空间,因此需要定期清理。_images/intro_cq_data.pngdata文件夹 _images/intro_event_log.png日志定时清理

耗用资源的操作错开进行【版本限定】

骰娘初始会设置在四点执行一次自动保存,五点时执行一次黑名单群清理。如果多只骰娘在同一系统上运行,同一时刻处理会集中在一点产生大量的资源占用,使CPU占用飚升。最好避免这种情形,比如令骰娘自动保存/清理的时间错开几分钟。

骰娘怎么重启后没保存?

骰娘在运行时产生的数据保存在内存中,需要保存到(硬盘)存档文件夹内,才能在下次启动时读取。酷Q在应用停用/退出时会调用内部事件,插件也会在此时自动保存存档,关机时终止酷Q进程不会调用酷Q的应用退出事件。因此不到万不得已,不要未关闭酷Q就重启设备

目前,部分修改频率较低的全局设置实现了即改即存,但角色卡属性等修改频繁的数据仍然需要在停用/退出时存入文件或手动保存。

保证插件的运行环境
监控CPU和内存占用【版本限定】

555版本后,插件会每半小时一次检测系统的CPU和内存占用。当占用率(百分比)超过设定的阈值(初始值90)时,会自动告警。服务器切忌等内存占用100%后再反应,否则可能无法远程连接成功,以免被迫重启服务器。选择一家省心服务商的省心服务器会降低很多不必要的精力耗费,免于发包被网关拦截、资源管理器内存随时间占用增长等烦恼。

服务器要多什么配置?

不考虑其他插件,Linux上运行骰子至少要128MB内存,Windows要1G内存;硬盘需要1G,容量越大,必要的清理文件频率就越低;CPU一般都够用。Shiki序列至今仍有一只官骰,蜗居在256MB内存的服务器上($11.25一年)。

控制所在群的数量低于500【仅限酷Q】

受接口限制,骰娘拉取的群列表长度上限为500。因此,当骰娘需要清查黑名单用户共同群或清群时,如果所在群数超过500,不在列表内的群就会免于清理。而这些群同样无法正常使用群管功能。因此,请定时清群。

Mirai不受此限制

怎样在运行同时登录骰娘QQ?【仅限酷Q】

Mirai默认使用平板协议,无需额外设置

对于酷Q:

QQ允许电脑端、手机端、平板端同时登录,因此可以使用与酷Q错开的登录设备来同时在线。酷Q默认使用QQ手机端进行登录,不影响主人使用QQPC版或QQHD登录骰娘QQ。如果一定要使用手机QQ登录,可以使用付费的酷Q pro并将所用QQ协议修改为HD版。

具体操作:

_images/demo_dev_platform.png酷Q协议

打开"[酷Q根目录]\conf\CQP.cfg"(使用记事本即可) 如果里面已经有[Dev]段,请在其下一行输入Platform=2 如果里面没有[Dev]段,请在文件最后加入

[Dev]
Platform=2

注: Platform=2代表采用安卓平板协议,Platform=0代表默认安卓QQ协议,Platform=4代表企业 QQ(普通账号无法登陆),Platform=5代表MacQQ(与 PCQQ 冲突)

骰娘怎么运营?

可以把自己做成骰娘吗?

不必须用小号,但是会影响你主号的使用,所以推荐用小号。为了避免因被禁言或踢出,制造不必要的麻烦,请提前关闭本体的不良行为监听,给不作为骰娘使用的群加免黑或忽略。由于随时有被腾讯封号的危险,请保证主号没有依赖于QQ的事务。使用酷Q air的情况下,注意有些功能是手机QQ限定的。

需要酷Q pro吗?

可选。发图、语音、点赞、撤回、禁言功能需要购买pro方可使用,但这均与Dice!核心功能无关

【时效性注意】考虑长期购买,最优惠的支付方式为先购买1个月(¥12)授权,然后每次续费6个月(¥54),以此方式,授权开销为¥3+9×授权月数

骰娘有什么注意/禁忌?

不要作弊!不要作弊!不要作弊! 公平随机是骰娘的生命。Dice!现在没有、将来也不会有操纵单个掷骰结果的功能。按经验,出千的骰娘需要向用户挨个谢罪并自愿█████,请不要放过这样的骰娘。

除此之外,骰娘是主人的私有物,完全受主人意愿支配。但如果要在非自己的群使用,也要注意考虑其他用户的体验。如:保证其他所有插件的功能在群内可开关,提前告知会调用群管理权限的功能,可以正常响应送出指令(禁止禁止!dismiss),不会频繁回复常用语句,避免使用禁言或踢出功能的插件,窥屏避免随意插话和多余操作,等等。

限定骰娘的服务范围【可选】

不少公骰在默认协议的基础上追加“禁止非跑团群使用”的内容,既因为这是Dice!骰娘设计的出发点,也为了让骰娘留在一个关系密切的用户网络内,这样协议的效力相对有保障。跑团用户从素质上并不比其他用户更守序,但因关系网络的存在而更易接受协议约束。对于一名和骰娘用户群关联甚少的陌生用户而言,被骰娘停止服务是一项零损失的惩罚。这也是邀请者需要为不负责任地邀请承担连坐的一项理由——不负责任地邀请导致骰娘在陌生且危险的群体中流传。

需要有自己的骰娘群吗?

强烈推荐为自家骰娘运营一个用户群,只要骰主认为用户数足够多。专门的用户群可以方便骰主及时通知骰娘冻结、维护等消息,处理许可申请、解黑等事宜,与用户协商沟通骰娘个性化细节,发布自定义列表,也更容易培养有黏性的忠诚用户。一批忠诚用户比几倍的路人用户更有价值:更有可能遵守用户协议;加入不可控陌生群的风险更低;可以更稳定地吸收用户;更容易得到打赏或推出其他增值服务,等等。

骰娘可以用来营利吗?

对于使用Dice!及其任何衍生版本的骰娘而言,可以。Dice!源码遵循 AGPL3.0 协议,不限制复制、修改用于商用,但需要保留原作者署名。骰娘一般通过开放二维码投食来平衡服务器的支出(新年、情人节等节日是暗示投食的好时机)。骰主也允许收费提供掷骰服务,当然,就供求现状而言,要以这种方式盈利似乎有困难。

注意,如果要商业化,除了掷骰插件,请注意骰娘其余部分有无商用限制,比如图像、文本创作/翻译内容、其他插件。即使非营利,也请避免因盗图、盗人设产生的纠纷。

投喂码
欢迎投喂Shiki,最好备注称呼和QQ

五、骰娘怎么调教

如何个性化回执文本?

自从V2.4.0开始,推荐使用菜单-综合管理-自定义回复来直接设置回复

除此之外,你也可以使用str命令:

str是一个较为特殊的指令,因为前缀str既是指令名,又是参数的一部分。str指令的格式如下:

.[条目名] [文本]	//定义文本
.strSelfName 本骰娘	
.[条目名]	//查看文本
.[条目名] reset	//查看文本
.[条目名] NULL	//定义文本为空

​ 例:_images/demo_str.pngimg

转义

str文本中,{}表示文本变量替换,如{nick}会自动转义成用户昵称。一部分变量是通用的,如{nick}, {pc}, {at},另一部分则只在部分场合有定义,如{res}。默认文本使用的变量一定是有定义的。完整条目见Shiki_Master_Manual.pdf->自定义回执文本

​ 例:_images/demo_strJrrp1.pngimg

全局设置怎么调?

自从V2.4.0开始,推荐使用菜单-综合管理-Master设置来调整全局设置

除此之外,你也可以用admin命令:

.admin [参数名] //查看设置

.admin [参数名] [参数值] //修改设置

完整条目见Shiki_Master_Manual.pdf->全局设置

全局设置指令
如何全局禁用.draw/.jrrp/.send?

自从V2.4.0开始,推荐使用菜单-综合管理-Master设置来调整下面三个选项,或者使用命令:

.admin DisabledDraw/DisabledJrrp/DisabledSend 1 开启时表示禁用

##### 如何用Dice!拦截其他插件?【版本限定】

开启DisabledBlock,会在群内停用指令时拦截低优先级插件。

.group +拦截消息 ,会在群内拦截低优先级插件。

牌堆怎么装?【版本限定】

通过将牌堆文件放入文件夹DiceData\PublicDeck\令骰娘读取,可以添加骰娘.draw可抽取的牌堆。牌堆文件夹可以在官方网站内获取,也可以自制。

_images/intro_public_deck.png牌堆文件夹

_images/faq_deck_error.jpg牌堆故障

角色卡模板怎么装?【版本限定】

内置卡模板仅有且默认“COC7”,额外模板需要参考样例制作并放入DiceData\CardTemp中,要求文件编码为GBK。每个文件对应一个模板,一个模板可以由多个文件配置。

_images/intro_cardtemp.png角色卡模板

读取已存在的角色卡模板时,别名、变量、默认值等采用增量加载的机制,各文件的条目将合并。展示列表采取覆盖加载,同名的生成选项采取覆盖加载的机制,可以将追加生成选项置于独立文件。加载按文件字典顺序,先加载当前目录文件,后递归加载文件夹内文件

利用模板生成预设卡参考"COC7-预设调查员.Dxml",生成非典型调查员参考"COC7-神话生物.Dxml",制作规则扩展参考"COC7-西藏之谜.Dxml"

文件为类XML格式,不符合XML规范,特别体现在没有&的转义

如果读取角色卡时无对应模板,角色卡内部会以“COC7”为模板

附录

链接

Dice!链接(可以加官方(水)群: 882747577或者Shiki群: 754494359) 官方网站: https://kokona.tech

Dice! Master手册

手册更新时间:20220607

这是Dice!于2022.12.15更新2.6.6rc(638)后对应的Master手册
用户指令请参考用户手册
更多内容可参看Dice!论坛
本手册中[DiceData]一律指代Dice!存档目录,当前版本格式为[框架根目录]/Dice[DiceQQ]

前言

感谢您选择Dice!
此手册将帮助您了解Dice!的基本使用和配置
如果在使用中遇到任何问题或者有任何建议,请在阅读文档后访问Dice!论坛提出你的想法
如果你认为此文档中有任何错误或值得改进的地方,欢迎点击右上方 Edit on GitHub 按钮修改文档。或者你也可以在论坛提出你的想法
祝跑团顺利!

更新说明(2.6.6)

2.6.5

  • 重写认主机制
  • 恢复cloud update远程更新
  • reply与msg_order合并,新增限制项lock
  • 优化消息转义,支持case、grade条件输出等,支持welcome有限转义
  • 留档每日数据至/user/daily/
  • 优化mod on/off热插拔,支持mod get安装/info信息/detail详细信息/delete卸载/reload重载/update更新/reinstall重装
  • WebUI新增模块管理页
  • mod新增image与audio子目录
  • 支持TimeZoneLag项手动调整时差
  • 新增各项lua交互特性,新增函数getSelfData,msg:format等
  • json等数据结构采用fifo特性

2.6.4

  • WebUI优化回执跨行编辑,同步reply的触发词多对一
  • 新增.mod on/off/list指令
  • mod新增event子目录,支持循环cycle/定时clock/代理hook类型事件
  • 更新log指令,允许自定义log名
  • 更新ob指令
  • 新增.link state/list

2.6.3

  • 新增DisableStrangerChat禁用非好友私聊
  • lua允许向msg读写成员,支持msg:echo方法
  • 扩展mod目录结构,支持reply/script/speech子文件夹
  • 优化文本转义,支持花括号多层嵌套,支持{vary:uid?}对用户差分
  • 优化关键词回复的解析,支持一个回复对应多个关键词,指令设置时以 | 为分隔符
  • http.post支持定义contentType
  • 修复.sc使用给定理智值时,剩余理智存入角色卡的bug
  • 修复[敏感词检测](#自定义敏感词库(.admin censor))时点(点号开头、指令、回复、被at时)

2.6.2

  • 可使用.str reset指令重置过时的文本而无需重启了
  • reply新增触发限制条件reply set时可加入Limit字段
  • 优化.setcoc指令,可使用.setcoc show查看
  • getUserConf支持获取用户名称
  • getGroupConf支持获取群名片/群权限/最后发言
  • WebUI页面为Master配置项添加说明
  • 角色卡、用户配置、群配置支持读写table
  • lua新增函数log,用于向框架或通知窗口输出日志
  • 允许设置全局默认rc房规
  • 允许lua修改5级以下信任

2.6.1

  • lua新增http.get/http.post等函数
  • 对lua优化require路径,现在require可以读取Diceki/lua/文件夹下的lua或dll了
  • CustomMsg不再兼容远古版本的序号转义(形如{0}{1}),请手动调整
  • 新增.bot信息抬头strBotHeader,请自定义(默认发布版写作"标准型",测试版写作"试作型")
  • 自定义回复新增前缀匹配模式
  • 新增.ak指令
  • WebUI提供reply跨行编辑按钮

2.6.0

  • ww指令机制优化
  • 支持reply使用多种触发模式与回复模式
  • 可使用WebUI进行远程图形化配置,原设置插件/综合管理重定向至WebUI
  • .pc stat角色卡掷骰统计

QQ框架(Dice!2.5.0+)

2.5.0版本,Dice!本体由酷Q插件改为了独立于框架的dll,由作为框架插件的DiceDriver加载,具体能实现的接口视框架条件而不同。先驱跑路后,Dice!驱动器目前支持Mirai、MyQQ、MyQQA、OnoQQAir、go-cqhttp框架,也在跟进OvQQ等更新。由于不同版本适配进度不同,你可能无法找到以下所有版本。Miria加载DiceDriver须使用MiraiNative插件。

QQ登录协议

同一QQ在同一类型设备(手Q/电脑/平板)登录时会将原先设备挤占下线,因此骰娘在框架登录所用设备应与人工登录所用设备错开。

选择一个框架

MiraiAndroid是当前不使用模拟器在手机挂载Dice的唯一选择,MiraiDice也支持在Linux、MacOS等系统挂载Dice,详见Dice! 论坛 Mirai版教程

框架接口差异
  • Mirai2.*(MiraiNative转接):
    1. 支持手Q/平板登录
    2. 不能多开QQ,启动即登录
    3. 无法处理登录设备异常(但支持设备锁验证)
    4. 不能响应来自自己的消息
    5. 无法获取未加入群的群信息
    6. 支持Windows, Linux, MacOS, Android, iOS(停更中)
  • HiMyQQ
    1. 由于程序不支持附加调试,存在未排除的异常风险
    2. 固定PC设备登录,协议可以选PC版QQ或者PC版TIM等
    3. 允许多开QQ,支持扫码登录
    4. 无法私聊发送语音文件
  • MyQQA(MQ安卓版):
    1. 由于程序不支持附加调试,存在未排除的异常风险
    2. 支持手Q/平板登录
    3. 允许多开QQ,但不支持启动自动登录
    4. 选择手表协议登录时支持扫码登录
    5. 接收好友申请时无法获取验证信息
    6. 因为程序缺陷,会出现连接服务器失败、“与服务器北京时间相差超过1秒”等问题
    7. 能接收频道消息,但发送频道消息疑似需要申请官方接口
  • go-cqhttp: 2. 支持手Q/平板/手表登录 3. 不能多开QQ,但DiceDriver支持一键启动多个go-cqhttp.exe 4. 支持扫码登录

框架目录结构

Mirai目录_images/intro_mirai2_dir.png

非Windows系统可能目录稍微不一致,但文档中提到的应该适用于任何操作系统。Android版目录位置为内部存储空间/Android/data/io.github.mzdluo123.mirai.android/files/

2.5.0+版本升级

  • Mirai版本:执行Update.sh/更新.cmd
  • MiraiAndroid: 安装新版APK
  • 其他版本: 需要将更新版的w4123.Dice.dll放入Diceki文件夹替换旧文件,并重载DiceDriver插件;如果DiceDriver同时有新版则必须同步更新
  • 请关注论坛以获得具体版本的更新方法

从低版本升级到2.5.0+

迁移骰娘存档到整合包

将对应版本的存档目录[DiceData]复制到新框架根目录下。

  • 原Dice2.4.0及以上版本:根目录下的Dice[DiceQQ]文件夹
  • 原Dice2.3.8exp9及以上版本酷Q骰娘:根目录下的DiceData文件夹_images/demo_cq_dicedata.pngCQ根目录下的DiceData文件夹

如为原Dice2.3.8exp10(555)及以下版本:将data/app/com.w4123.dice复制到新目录[DiceData]/com.w4123.dice的位置。_images/demo_cq_appdata.pngdata/app/com.w4123.dice

Mirai框架升级
  1. 按照论坛中教程,使用新安装方法安装新版本Mirai
  2. 迁移Dice[DiceQQ]目录
  3. 将根目录下device.json复制到根目录下(让Mirai继续使用先前的登录信息)

WebUI配置面板(推荐)

WebUI是全平台可用的图形化配置页面,通过Dice!初始化时运行的监听端口访问。

WebUI 基础使用

在登录成功以后,Dice!会向Master(没有Master的情况下,自己) 发送WebUI运行的端口。如图中运行在8080端口。默认情况下,WebUI运行在一个随机的未使用的端口,但你可以通过WebUIPort属性更改。

_images/WebUI_init.png启动初始化

在本地(运行Dice的同一个设备)浏览器中输入http://127.0.0.1:端口进入WebUI 请一定要输入http://否则某些浏览器会卡死
.system gui指令也会引导打开WebUI
如果你在远程访问WebUI,请确定已配置好防火墙等,并使用对应的IP地址或域名访问(而不是127.0.0.1) _images/WebUI_login.png端口访问WebUI

提示登录时,默认用户名为admin,密码为password,进入WebUI后可在WebUI配置栏更改密码。WebUI默认只允许本地访问,所以如果设备只有你一个人能访问(不是共享的云服务器等)不更改密码也无所谓。 _images/WebUI_main.png主界面

可以在上面执行管理操作,应该都符合直觉。需要注意的是你可以直接点击表格修改其中内容,表格中的修改无需点击保存等,会在编辑完成后自动生效。

WebUI 配置

WebUI有三个配置项,更改配置后重启才会生效
EnableWebUI - 启用WebUI, 0为禁用, 1为启用, 默认为1
WebUIPort - WebUI端口, 0为随机, 其他为固定端口
WebUIAllowInternetAccess - 允许从非本地地址访问WebUI, 0为不允许, 1为允许, 默认为0, 也就是WebUI默认只能从本地访问。如果你使用Docker网络等,你可能需要将此项设置为1才能在Docker宿主机上访问WebUI。

WebUI 环境变量

可以通过环境变量设置WebUI配置。环境变量具有较高优先级。
DICE_WEBUI_PORT 设置端口,0为随机端口
DICE_WEBUI_PORT_USE_VARIABLE 使用另一个环境变量的值作为端口,比DICE_WEBUI_PORT有更高优先级,可用于PaaS等平台
DICE_WEBUI_ALLOW_INTERNET_ACCESS 0为不允许, 1为允许

配置远程访问

WebUI 使用 HTTP Digest验证,密码保存为Digest,理论上比较安全。然而,如果需要暴露到公网,我们仍然推荐使用nginx或apache等配置好TLS然后反向代理到Dice WebUI以确保安全。
当然,允许远程访问前请务必更改密码,不然再安全也没用。

GUI管理面板(已停更)

任意Windows框架可对骰娘发送.system gui打开图形界面(确认进入GUI,取消进入WebUI)。请注意此页面仍可使用,但将不会继续更新并有可能在未来版本移除,推荐使用上方提到的WebUI进行设置修改。

(MiraiNative)右键托盘图标(流泪猫猫头)->(插件菜单)Dice!->综合管理_images/demo_mirai_gui_entry.pngMirai面板入口

(OQ等)切换至插件列表页->Dice.Driver(右键)->设置插件。单框架多开时请使用指令,避免错乱

可在界面内设置Master、调整用户信任、修改CustomMsg文本、修改全局配置

_images/demo_gui_page2.png图形界面2

骰主(Master)

Master是骰娘的控制者,每个骰娘同时至多只能有一个Master。Master可以控制骰娘的发言和行为,并进行大量个性化配置,也对骰娘行为负责。受信任用户也可以获得对骰娘的部分权限,但只有Master拥有发放和回收高级权限的权限。

新生骰娘默认为无主状态,你可以在WebUI/GUI管理面板完成认主(推荐,WebUI支持全平台)或在聊天栏输入口令认主。无主骰娘在启动时将向自身账号及框架日志窗口输出认主口令,在聊天栏中对骰娘发送对应口令将绑定发送者账号为Master。也可直接在管理面板设置Master。认主时将自动开启全类型通知窗口,建议将部分通知特别是通知窗口0转移到专门的小群
.master delete 解除绑定,骰娘此时会重回无主状态,清空通知窗口,但先前设定的配置不会初始化。
.master reset [新masterQQ] 解除绑定,骰娘此时会认主[新masterQQ](原Master保留信任级别)
.master admin添加管理时自动将私聊添加为监视窗口,管理可以用.admin delete放弃权限,清除窗口可以私聊.admin notice - me

控制台指令

状态查看

.admin state 可以查看当前的全局设置和黑白名单情况

遥控开关

  • .admin boton/botoff [群号] //等效于所在群群管使用.bot on/off
  • .dismiss [群号] //可以遥控骰娘退出所在的群,即使骰娘不在,也能将该群移出白名单

消息发送(.send)

send用于用户与管理员间的远程交流
.send 待发送消息 (任何人可用)向Master发送消息
.send [窗口] [待发送消息] //向指定窗口发送消息(权限4限定;权限5用户发送的消息不会标明转发来源,等效于骰娘亲自说话)

窗口是指QQ收发消息的聊天窗口。窗口参数识别一下6种格式:

  • qq [QQ号]
  • group [群号]
  • discuss [讨论组号]
  • notice [通知类型](仅send可用)
  • this(发送指令的窗口)
  • me(发送者的私聊窗口)

系统指令(.system)

.system save //立即存储所有数据(相当于执行所有停用应用时的操作)
.system load //立即读取外置文件(模块、牌堆和角色卡模板)。便于不重载应用的反复调试。save/load涉及的文件见附录。
.system state //显示插件运行时间及内存占用等硬件信息
.system rexplorer //杀死资源管理器后重启。当 Windows 服务器内存占用逐渐随时间而升高时,考虑由资源管理器逐渐占用内存导致,可能适用该指令。权限5可用

一键清群(.master groupclr)

遍历群列表并退出符合条件的群,也可以在应用菜单中【一键清退】。部分框架群列表上限500,请避免群数超过500。单次清群上限受GroupClearLimit调节(默认一次至多退20个群)。
.master groupclr 可以实现一键退群,无参数时默认参数为unpower
.master groupclr [天数] 将退出当前所有骰娘在指定天数内未发言的群
.master groupclr unpower 将退出当前所有骰娘不是群管/群主的群
.master groupclr preserve 将退出当前所有许可使用的群
.master groupclr black 将退出当前所有
黑名单群
有危险的黑名单用户的群

全局配置

配置项目表见附录
.admin [关键词] //查看配置项状态,如.admin Private
.admin [关键词]=[数值] //修改配置项。一般的开关项1表示开启,0表示关闭

通知窗口

运行过程中产生的提醒、警告等通知消息会分类型发送给设置的通知窗口。所有通知被分为0-9共十个等级,其中0-5会被骰娘用于内置广播,6-9用于自定义(具体通知类型见附录)。当产生通知时,会向通知窗口中所有接收该类型通知的窗口发送消息。如果没有窗口能发送消息,骰娘会将通知发送给自己;无论如何所有通知会自动保存在"[DiceData]\audit"中,请定期整理该文件夹。 由于通知窗口0会产生较多日志型消息,建议将通知窗口0转移到专门的小群
.admin notice [窗口] +/-[通知类型](...+/-[通知类型]) //增减通知窗口类型
.admin notice - [窗口] //直接移除指定窗口。
.send notice [通知类型] [通知文本] //向指定类型窗口广播通知

例:
.admin notice group 928626681 -0-1-2-3-4+5 变更通知类型的接收情况
.admin notice - this 直接移除当前通知窗口

全局开关(.admin DisabledGlobal=0/1)

DisabledGlobal=1等价于.admin off(全局关闭)。开启时一切如常,关闭则进入静默状态,所有群聊/讨论组会相当于.bot off状态而无视群内设置,私聊会回复关闭中的信息。也可以用.admin on/off 操作全局开启/关闭,也可以在应用菜单中点击"全局开关"自动切换。

定时点事件(.admin clock)

.admin clock +/- [任务名] [时]:[分] 添加/移除 定时任务
内置定时任务:on-全局开启;off-全局关闭;clear-黑名单清群
允许调用自定义任务,参考plugin手册
每日执行,如果当日错过不会补回。
定时开启和定时关闭时会向窗口4发送通知文本
清群为基于黑名单清群(=.master groupclr black)

定时长事件

以下事件会在Dice!每运行固定时间后执行一次
.admin AutoSaveInterval=5 //定时保存用户数据(min),默认每5分钟保存一次
.admin AutoRemake=0 //定时重启(h),默认关闭。
定时系统监测报警(.admin SystemAlarmRAM/SystemAlarmCPU/SystemAlarmDisk=90) :每半小时监测一次系统内存、CPU、硬盘占用(%),超过阈值即报警。报警状态下每5分钟监测一次,如果数值升高超过1个百分点则继续报警,低于阈值则会提醒解除报警。设置在1-99时生效。

用户策略

用户记录(.user)

用户在使用指令或被授予信任后会生成用户记录
.user state //查看自己用户记录
.user trust [用户qq] //查看用户信任级别(4以上限定,上级对下级屏蔽)
.user trust [用户qq] [信任级别] //调整用户信任级别(4以上限定,只能调整下级用户,最高为255)
.user diss [用户qq] //最轻的拉黑用户(type=local,danger=1)

用户授信(.user trust)

每名登记用户会标记信任级别,初始为0。级别越高享受的权限越多,上级包含下级的所有权限。

.admin whiteqq (-) [白名单QQ] 可以为用户授予信任1(带减号为收回信任)

信任级别权限
1(白名单)为群分发使用许可,允许使用某些指令
2黑名单豁免
3响应退群指令,接受不良记录共享(warning)
4(后台管理)查看、分发和收回低于4的用户权限,原管理权限
5(骰主级)查看、分发和收回低于自身的用户权限,系统操作指令

过期数据自动清理(.admin InactiveUserLine/InactiveGroupLine=360)

最后活跃天数超过InactiveUserLine的用户,其记录及角色卡会在清理用户时被清除;最后活跃天数超过InactiveGroupLine的群,其记录及开团信息会在清群时被清除。(配置项为0时不生效)

清理过期数据会在定时任务clear中自动执行,在执行清群时也会在退群的同时检测目标群是否会被清理。

监听好友申请(.admin ListenFriendRequest=1)

默认开启,仅好友验证方式为【需要验证信息】或【需要回答问题并由我确认】时有效,拦截消息并决定通过或拒绝。黑名单用户必定拒绝。同意好友邀请时将发送strAddFriend,特别地,通过受信任用户的好友时会发送strAddFriendWhiteQQ。当好友验证方式为【允许任何人】或【需要正确回答问题】时,通过开启ListenFriendAdd,也会向通过的好友发送strAddFriend,注意这种方式通过的好友可能仅为单向好友。

允许陌生好友邀请(.admin AllowStranger=0/1/2)

默认为1。决定是否接受陌生人的好友邀请:2-无条件接受;1-无用户记录不接受;0-无信任不接受。该项设定建议配合QQ自带的【允许陌生人邀请我加入群聊】选项。

单群设置(.group)

.group ([群号])+/-[词条] //修改单群设置,群内原地使用可省略群号

词条效果修改条件
许可使用私用模式/审核模式可正常邀请入群/群内使用信任1
未审核审核模式下标记新加入无【许可使用】的群,拒绝指令信任1
免清清群时和搜查黑名单时略过该群信任2
免黑发生在该群的不良记录无效;不搜查黑名单信任4
忽略不接受任何该群的事件信任4
协议无效不接受除云黑外的事件信任4
停用指令停止响应指令(.bot off)信任或群管
禁用回复停止响应回复(.reply off)信任或群管
拦截消息拦截消息,阻止低优先级插件响应信任或群管
禁用jrrp/禁用me/禁用ob/禁用draw/禁用help禁用对应指令信任或群管 |
  • 原群白名单自动初始化为【许可使用】+【免清】
  • 重要的大群(跑团交流群、骰娘群)请加【免清】+【免黑】或【忽略】,以免意料之外的自动退群
  • .admin whitegroup 查看当前【许可使用】或【免清】或【免黑】的群列表
授权许可(!authorize)

受信任用户可通过在群内发送!authorize或在任意窗口发送!authorize +[群号]来为目标群添加【许可使用】。非信任用户也可在群内使用!authorize [理由]向窗口2发送许可申请。
!authorize (+[群号]) //受信任用户为本群+许可使用
!authorize (+[群号]) [理由] //非信任用户申请本群许可
撤销许可使用需要使用.group [群号] -许可使用

批量群处理(.groups)

取群列表.groups list(管理限定)
.groups list idle //按闲置天数降序列出群
.groups list size //按群规模降序列出群
.groups list [群管词条] //列出带有词条的群

监听群邀请(.admin ListenGroupRequest=1)

默认开启。接收用户的群邀请(小群邀请将由QQ系统自动通过,因此骰娘无法处理或知晓邀请人),拒绝来自黑名单的邀请,邀请来自受信任用户时通过且自动为群添加【许可使用】,非信任用户时视私用模式开关状态处理。通过邀请的同时标记邀请人。如果回执为【已同意】却实际并未入群,考虑接口出错或腾讯屏蔽。

入群检测(.admin ListenGroupAdd=1)

默认开启。入群时反馈群信息(群名、群号、群人数、邀请人),遍历群员,获取群主信息(临时创建的群可能无法获取群管理信息)、检索黑名单,为私用模式二次确认【许可使用】,为审核模式检查【许可使用】;群内发现黑名单时会通知,有管理员是黑名单时会触发退群(群未设置【免清】或【免黑】时)。发送strAddGroup。

私用模式(.admin Private=1)

认主时私骰作成进入私用模式,仅在受信任用户或【许可使用】群邀请时接受邀请,在新加入管理员无信任用户(否则为群自动添加【许可使用】)且无【许可使用】的群时自动退出,退出时自动留言。也可以用.admin only/public 切换私用/公用状态。

审核模式(.admin CheckGroupLicense=0/1/2)

入群后,对于无【许可使用】的群,将自动标记【未审核】并发送strAddGroupNoLicense。该项生效时将拒绝提供除help之外的其他服务,其余指令仅authorize/dismiss/bot有效。该项为1时拒绝所有【未审核】的群(不溯及审核模式开启前已加入的群),该项为2时拒绝所有无【许可使用】的群(含开启前已加入的群)。

审核模式与私用模式的差别

  • 审核模式会在未许可群停留,增加潜在风险;
  • 留在群内有更高向用户群引流的概率;
  • 由于自动入群的小群无法正常给群加【许可使用】,骰娘在无法识别群管理的情况下会自动退群,需要受信任用户额外使用一次!authorize [群号],重复邀请;

黑名单(.admin blackqq/blackgroup)

任何情况下,来自黑名单的邀请不会被通过,指令除了dismiss都不会被响应
.admin blackgroup (-) ([黑名单理由]) [黑名单群号] 可以添加群黑名单(带减号是删除)
.admin blackqq (-) ([黑名单理由]) [黑名单QQ] 可以添加用户黑名单(带减号是删除),带理由时危险等级为2且通知对方,否则危险等级为1。
.admin blackfriend //查看好友列表内的黑名单用户
危险的黑名单用户包括群内权限更高的用户或非免清群内相同权限用户(可选),同时危险等级需要达到2。危险等级只有1的黑名单用户不通知、不会触发退群。
现在自己、Master、信任2以上用户都不会新加入黑名单。

不良行为检测(.admin ListenGroupKick/ListenGroupBan/ListenSpam=1)

默认开启。分别开启后,移出、禁言、刷屏会被加入黑名单。生成危险等级2的不良记录后,会自动向通知5窗口广播生成的!warning指令。warning指令:接收到信任3或记录可识别的骰娘warning时,骰娘将自动录入黑名单。骰娘通过这一机制实现不良记录的传递共享。
拉黑群时会自动移除【许可使用】。
刷屏在程序内定义为发送指令超过20/30s,实际解释权归Master所有

邀请人连带(.admin KickedBanInviter/BannedBanInviter=1)

默认开启。被移出/禁言时将入群邀请者同时加入黑名单。
邀请者责任源于协议中不得擅自拉群的规定,这里的擅自是双向的:一边无视骰娘协议,一边无视群内意愿

云操作(.cloud)

查看更新(.cloud update)

注意:此功能可能暂时不可用。对于Mirai请执行更新.cmd或Update.sh以进行更新。
远程获取Dice!正式版及开发版信息
.cloud update //检查版本更新
.cloud update release //下载最新发布版
.cloud update dev //下载最新开发版(使用测试版/开发版请保持与开发者的交流)

同步不良记录(.cloud black)

该指令会从后台获取经核验的不良记录,并更新本地黑名单库。骰娘不会自动同步云端记录。

不良记录

不良记录上传(.admin CloudBlackShare=1)

该选项默认开启
开启时,当骰娘原生生成Kick/Ban/Spam记录时,会同时向后台发送该记录。记录在后台收录后处于待核验状态,需要后台管理二次核验才会对其他骰娘生效。
所有云记录有唯一的wid。上传可能因连接后台失败而失败 对于生成记录过于频繁,或不良记录源于非Dice!原因与用户产生不良互动的,后台会拒绝收录该骰娘的记录。

同步云不良记录同步失败”的处理方式:手动访问url指向的网页,本地另存为后,参考下文【读取外源记录文件】的方式手动读取。

该选项开启时,其他QQ发送云端已核验的记录也会对该骰娘生效。

读取外源记录文件

将其他来源的不良记录BlackList.json重命名为BlackListEx.json,放置于[DiceData]/conf目录下。Dice!加载时会自动读取并更新记录,之后该文件会被销毁。两种情况下已有记录不会在读取时更新:DiceMaid就是自己;危险等级与本地记录不同。

云记录注销

当前记录核销在群(928626681)内完成,入群后可使用以下指令:

  • isbanqq=[qq] 查询目标QQ的云记录
  • 查询wid=[wid] 获取wid对应的记录
  • erase=[wid] 注销wid对应的记录,仅记录中的当事骰娘(DiceMaid)和骰主(masterQQ)可用

可私聊司令塔BotShiki(Pope)(530136753)(具体角色可能会有变动)进行上述操作。也可请求管理来完成注销,最通用证明材料为:当事人对当事骰娘发送.admin isban [本人QQ],连同骰娘回复将聊天记录转发给管理。

用户自行解黑

云记录核销之后,所有骰娘并不会自动同步。遇到尚未注销黑名单的骰娘,请用户自行解黑。方法为对已注销的骰娘发送.admin isban [QQ],将得到的warning指令保存,用于私聊未注销的骰娘。由于骰娘存在连接后台失败的可能,导致无法获取记录更新的信息,因此无法保证指令有效。这时请联系相应骰主。

其他机制

框架消息富文本转义(CQ码)

DiceDriver会将不同框架下接收的QQ富文本消息(图片、语音等)转换为统一的CQ码,发送时则会转义回框架格式

[CQ:image,file=xxx.abc] 将本地[框架根目录]/data/image/xxx.abc位置的图片上传并在消息中发送

[CQ:image,id=xxx]接收消息中的图片,id与服务器缓存图片对应,但区分群聊图片与私聊图片。群聊调用私聊消息的图片id将无法正常发送图片,反之同理。

[CQ:record,file=xxx.silk]将本地[框架根目录]/data/record/xxx.silk位置的语音上传并覆盖整条消息内容发送(Mirai不限制文件后缀)

[CQ:face,id=xxx] 发送对应id的QQ黄豆表情

[CQ:emoji,id=xxx] 发送对应十进制编码的Unicode字符

[CQ:reply,id=xxx](群聊且限定在开头适用)回复引用指定id的消息

消息送间隔(.admin SendIntervalIdle=500)

一般地,Dice!的待发送QQ消息不会立即发送,而是进入发送队列排队发送,并在每次发送后等待固定时间,即发送间隔(ms)。SendIntervalIdle控制闲时发送间隔,SendIntervalBusy控制忙时发送间隔。

特别地,Dice!识别一条待发送消息中的分页符('\f'),并沿分页符将消息拆成多条,依序送入消息发送队列。分页符无法在指令输入时直接录入,可以输入{FormFeed}作为消息分段,调用时会自动转义。

回复引用原消息(.admin ReferMsgReply=1)

(仅gocq及MQ系列可用)开启后所有公屏消息回复将引用原消息。

本系骰识别

Dice2.5.0+可以通过调用后台接口以识别目标QQ是否在Dice!云端有登记信息。新入群遍历群成员时将统计可识别本系骰数量。由于禁言反制和踢出反制是Dice!的默认协议,让Dice!骰去禁言/踢出另一只Dice!骰无疑是愚蠢的,因此在执行group ban/kick前会先查询目标是否为Dice!骰,是则不会进行实际操作。基于骰主所拥有的隐私权,云端不可见(CloudVisible=0)的Dice!骰不会被识别。

个性化

文本转义标识

回复文本中可以通过特定的{}标记转义文本,当前花括号支持嵌套。

指令回复中对用户(消息来源)的通用转义如下:

  • {nick}-取用户昵称,优先级为群内nn>全局nn>群名片>QQ昵称。
  • {pc}-取用户角色名,未录入角色卡则同{nick}。

部分转义方法:

  • {at:目标用户}-at用户,省略参数则at消息对象;

  • {help:条目名}-获取帮助文档指定条目;

  • {ran:最小值~最大值}-从范围中随机取值;

  • {sample:分项1|分项2(...|分项n)}-从所有分项中随机均匀抽取一项插入文本。例: {sample:效果拔群|干得漂亮}

  • {case:uid?账号1=1号专属文本&账号2=2号专属文本&else=通用文本}-根据消息语境中的变量分条件回复;

  • {grade:user.favor?0=用户&100=亲爱的&else=讨厌鬼}-根据消息语境中的变量分档位回复;

  • {wait:5000}-处理时延迟指定的毫秒数

  • {var:变量名?值}-赋值变量并打印

    {var:变量1=值1&变量2=值2}-多变量赋值,不打印

其他转义标识:

  • {br}换行
  • {FormFeed}分条发送

扩展模块mod

扩展模块放入[DiceData]/mod/读取,可加载自定义回执、自定义回复与帮助词条,详见开发手册。mod按序读取,且从后向前覆盖。

.mod list 查看已加载mod列表 .mod on 模块名 启用指定模块 .mod off 模块名 停用指定模块 .mod info 模块名 指定模块简介信息 .mod detail 模块名 指定模块详细信息 .mod delete 模块名 卸载指定模块

远程模块资源

_Static/WebUI_mod_source.png远程资源界面

可以在WebUI->模块管理->远程资源中查看远程mod库列表并远程安装。

个性化WebUI

Dice!2.6.6更新后将WebUI主页面写出到[DiceData]/webui/index.html,同时webui目录可通过WebUI端口访问。

自定义

扩展指令

扩展脚本可放入[DiceData]/plugin/读取。文件样例见附录,更多内容请参阅脚本手册。默认可使用.help扩展指令 查询载入的指令。

自定义帮助词条(.helpdoc)

.helpdoc [词条名] [词条内容] —— 自定义帮助词条
词条内容以&开头表示重定向,如.helpdoc 追仙子 &追仙后,.help追仙子 将重定向到追仙的词条
词条名不需要是源代码中已有的,请随意添加

特别地,部分词条会存在引用的情况,自定义时可以只自定义被引用的词条。

{
    "设定":"Master:{master_ID}\n好友申请:需要使用记录\n入群邀请:黑名单制,非黑即入\n讨论组使用:允许\n移出反制:拉黑群和操作者\n禁言反制:默认拉黑群和群主\n刷屏反制:警告\n邀请人责任:有限连带\n窥屏可能:{窥屏可能}\n其他插件:{其他插件}{姐妹骰}\n骰娘用户群:{骰娘用户群}\n私骰分享群:192499947\n开发交流群:1029435374",
    "骰娘用户群":"【未设置】",
    "窥屏可能":"有",
    "其他插件":"【未设置】"
}
自定义回复(.reply)

消息检测触发的顺序为特殊指令->自定义指令(Type=Order)>内建指令->自定义回复(Type=Reply),检测触发词的顺序为完全匹配->前缀匹配->模糊匹配->正则匹配触发回复也会算入刷屏计数!(脚本中可通过写入msg.ignored = true无视计数)

.reply set (Type=[回复性质](Reply/Order)) 
[匹配模式](Match/Search/Prefix/Regex)=[触发词1|触发词2]
(Limit=[触发限制])
[回复模式](Deck/Text/Lua)=[回复词]
  • 回复性质:决定该回复由停用指令(bot off)还是禁用回复(reply off)控制开关。该项可省略,默认由reply控制。
  • 匹配模式:Match-完全匹配;Prefix-前缀匹配;Search-模糊匹配;Regex-正则匹配。
  • 触发限制:可填入零至多项触发条件,多项条件以;分隔,在触发词匹配后检查是否会被触发(一项不满足就不触发)。不触发的消息不计入刷屏、不进入当日计数、不进入冷却计时
  • 回复模式:Deck-牌堆(回复词以分隔符'|'切割为牌堆)Text-直接返回回复词;Lua-将回复词作为Lua语句执行,将返回值回复。

.reply show [触发词] 查看回复注释
.reply get [触发词] 查看回复本文
.reply del [触发词] 清除触发词
.reply list 列出全部触发词

默认可使用.help回复列表 查询触发词,帮助文档对所有人可见,建议手动修饰。

触发条件
  • 用户名单user_id:

    • 正向名单user_id::只有所列ID中的用户才触发,ID以|分隔 user_id:触发用户1|触发用户2|...
    • 反向名单user_id:!:只有所列ID中的用户不触发,ID以|分隔 user_id:!触发用户1|触发用户2|...
  • 群聊名单grp_id:

    所有私聊的id视为0,写入正向名单表示不限制私聊,写入反向名单表示私聊不触发

    • 正向名单grp_id::只有所列ID中的群聊才触发,ID以|分隔, grp_id:触发群号1|触发群号2|...
    • 反向名单grp_id:!:只有所列ID中的群聊不触发,ID以|分隔 grp_id:!触发群号1|触发群号2|...
  • 触发概率prob:百分比概率触发 prob:概率

  • 冷却计时cd:计时器中有一个正在冷却状态则不触发,若触发则为每个计时器增加冷却时间。每个计时器以&分隔,计时器名称为空时默认取回复关键词。

    计量单位:chat-以聊天窗口为单位;user-以用户为单位;global-全局共享

    cd:[计时器名称]@[计量单位]=[冷却秒数]&...

    [计时器名称]可省略,视为取本回复的关键词;@[计量单位]可省略,视为@chat。特别地,当两者均省略时,cd:秒数表示以聊天窗口为单位,为本回复独立计算特定秒数的冷却。

  • 今日计数today:计数器中有一个达到上限则不触发,若触发则为每个计数器+1。每个计数器以&分隔,计时器名称为空时默认取回复关键词。

    today:[计数器名称]@[计量单位]=[计数上限]&...

    省略机制同cd。

  • 用户阈值user_var:UserConf中变量为特定值或以上或以下触发,每项以&分隔,字段=值表示等于时触发,字段=值+表示大于等于时触发,字段=值-表示小于等于时触发。特别地,trust表示信任等级 user_var:[变量1]=[阈值1]&[变量2]=[阈值2]+&[变量3]=[阈值3]-

  • 群聊阈值grp_var:群聊时取GroupConf,格式同user_var,使用时推荐搭配grp_id:!0排除私聊

  • 自身阈值self_var:取DiceMaid自身UserConf,格式同user_var

  • 骰娘识别dicemaid:根据对方是否识别为Dice!骰娘判别是否触发,消息来源为自己时不作判别

    • 仅骰娘触发dicemaid:only
    • 骰娘不触发dicemaid:off
自定义回执文本(.str)

将系统处理事件的回执替换为自定义文本
.[键值] [文本] ——自定义骰娘的某项回执
.[键值] reset ——重置为原生文本
.[键值] NULL ——自定义文本为空白
.[键值] show ——查看自定义文本

如果没有设置,strSelfName和strSelfCall将预设为QQ昵称,并且{self}会自动替换为strSelfCall,前者用于自我展示,后者用于自称。强烈建议自定义strHlpMsg和strAddFriend、strAddGroup,向不熟悉的用户介绍私骰的特别之处(申请、使用须知)。

例:

.strSelfName 测试姬 //入群等自我介绍场合使用
.strSelfCall 本骰娘 //回执中用作自称,{self}的重定向
.strBotOn {self}开始工作了
.strAddGroup Shiki(Judgement), Servant Ruler, 四季映姫·ヤマザナドゥ。来,细数你的罪孽吧
.strRollFumble 大失败 就像是见了阎王一样
.strNameSet 以后就称呼{old_nick}的名称为{new_nick}了

_images/demo_str.png

自定义敏感词库(.admin censor)

敏感词设置.admin censor
.admin censor +([触发等级])=\[敏感词0](|[敏感词1]...) //添加敏感词
.admin censor -\[敏感词0](|[敏感词1]...) //移除敏感词
例:.admin censor +=nmsl //将“nmsl”设置为Warning级
.admin censor +Danger=nn老公|nn主人 //将“nn老公”、“nn主人”设置为Danger级
.admin censor -手枪 //移除敏感词“手枪”

匹配机制

骰娘会模糊匹配指令标识符(.)开头的消息,并返回所含敏感词的最高触发等级
匹配过程自动跳过文本中的特殊符号和空格,且大小写不敏感
受信任用户会相应降低触发等级,信任4以上用户将不触发检测

触发等级

使用指令设置Ignore等级可以临时屏蔽词库中不该生效的词,对外置词库这一等级是无意义的
Ignore //无视
Notice //仅在0级窗口通知
Caution //提醒用户,并在1级窗口提醒
Warning //【默认等级】警告用户,并在1级窗口提醒
Danger //警告用户且拒绝指令,并在3级窗口警告
*请避免为纯字母/数字的敏感词设置较高触发等级,这些字符存在误匹配图片码的可能性

词库批量加载

load会读取存档目录中/conf/censor下所有文本文件
字符编码默认按GBK读取,若字符为UTF8,可在开头先起一行"#UTF8"
一词一行
触发等级默认为Warning,读取到"#[触发等级]"时,后续录入的词调整为响应等级,如"#Danger"

词库文件示例: 以下内容表示以utf-8编码,读取4个敏感词 "nmsl":Warning,"nn老公":Danger,"nn主人":Danger,"sb":Notice

#UTF8
nmsl
#Danger
nn老公
nn主人
#Notice
sb
外置牌堆

将牌堆文件放入[DiceData]/PublicDeck内并重启或load,即可使用牌堆内的条目。牌堆名以'_'开头时,无法直接使用draw命令抽取。

支持作者

溯洄正在为后续的开发计划众筹:https://afdian.net/@suhuiw4123

欢迎在爱发电支持Shiki:https://afdian.net/@dice_shiki

也可以微信赞赏Shiki:_images/wechat_pay_shiki.png

常见问题

登录失败

  • 当前版本过低[00020]:所有非本地原因的登录失败都会返回错误码00020,该错误码的官方说明即为“当前版本过低”,实际原因可能是账号登录被tx判定有风险等,可以选择更换登录协议等方法尝试。 _images/qqerror_00020.jpg00020
  • 登录环境被判定风险:Mirai或gocq的登录设备信息在device.json中,设备信息可能被标记风险,可通过删除账号的原device.json重新尝试(Mirai删除bots文件夹下账号信息)。
  • 骰娘账号手机登录时,在同一wifi下登录框架有更高成功率。若如此且框架为mirai或gocq,可在登录成功后将设备信息文件device.json迁移至用于稳定运行的网络环境(gocq可额外迁移登录会话文件session.token)。
  • gocq显示密码错误或账号被冻结但实际并没有:原理不明,常见于刚解除冻结后,可尝试删除config.yml中的密码改为扫码登录。

框架登录通过的成功率:同wifi下扫码登录>异地扫码登录>短信验证登录>无验证登录

账号风控(消息屏蔽)

账号被QQ屏蔽最常见的特征有:群聊消息无法成功发送,但私聊消息可以;长文本消息无法成功发送,但短文本可以。

验证是否被屏蔽最直接的方式为查看框架日志,如有发送消息的记录但实际并没有,则可以断定被屏蔽。除此之外的快捷检测方式为在群内发送暗骰指令.rh,如果私聊能正常收到结果但群聊无回执,则高概率为被屏蔽。

当账号被屏蔽时,建议暂停工作,避免带病上班,同时将账号作为正常QQ使用(养号)。

网络连接失败

  • 心跳报告失败:心跳只是向后台发送骰娘当前状态,不影响骰娘运行
  • jrrp获取失败:访问境外jrrp服务器失败会向后台报错,但用户会正常收到回执,不影响使用
  • log上传失败:访问境外log服务器失败,需要手动从\user\log\文件夹提取txt
  • 不良记录上传失败:无法生成云黑wid,可以在官群内发warning申请补录

命令行窗口无动静,按回车有

在窗口标题栏右键属性->选项->编辑选项页下,关闭快速编辑模式,确定。

Dice驱动器启动失败

[Fatal]: listen tcp 127.0.0.1:15800: bind: Only one usage of each socket address <protocol/network address/port> is normally permitted.

ws连接的端口被占用(极大可能是多个go-cqhttp程序使用相同默认的初始端口),需要修改config.yaml使每个go-cqhttp配置的端口各不相同:

# config.yml 底端部分
# 连接服务列表
servers:
  # 正向WS设置
  - ws:
      # 正向WS服务器监听地址,修改此处以避免冲突
      address: 127.0.0.1:15801
      middlewares:
        <<: *default # 引用默认中间件

磁盘空间占用增多

  • Mirai一键脚本使用.git更新文件,会造成.git文件夹堆积,如无回退需要可以直接删除;
  • Dice目录下user/log/所存储的.log记录文件不会在log结束后销毁,部分log可能存在长期记录而不被关闭的情况,需要手动清理;

忘记WebUI管理密码

删除conf/WebUIPassword.digest,密码将自动重置。

Mirai红字报错

W/MiraiNative: 当前运行环境 64 可能不与 Mirai Native 兼容,推荐使用 32位 JRE 运行 Mirai Native

Mirai启动时检测64位jre启动,而官方版本的MiraiNative只支持32位。请使用MiraiDiceWindows一键脚本部署后的更新.cmd,并确保根目录没有64位jre后以启动Mirai.cmd启动(该启动方式将强制使用根目录下的32位jre而非环境变量中的)。

W/MCL Addon: iTXTech Soyuz 未安装,Soyuz MCL Handler 特性已禁用

无影响。

附录

配置项目表

详见WebUIMaster设置注:Disabled是不可用的意思! 指令禁用对信任4以上用户无效 DisabledBlock仅在区分插件优先级的框架生效,高优先级插件将拦截事件而非传递给低优先级插件处理 对.me特别处理的理由是其在跑团中几乎零作用,却可以制造骰娘自己说话的假象,引发风险。

通知类型表

  • 通知级别:事件
  • 0(调试):启动初始化、自定义回执文本、接收响应的warning、非黑受邀入群、非黑好友申请、刷屏初次提醒
  • 1(提醒):解除禁言提醒、增减黑名单、接收warning处理反馈、申请/入群检测到黑名单、黑名单触发退群、接收陌生warning、自定义全局设置、信任等级变动、遥控单群退群/群设置
  • 2(敏感):撤销管理员、批量修改群设置、接收send信息、群聊清查
  • 3(警告):刷屏二次提醒、原生不良记录、系统负载报警
  • 4(用户通知):切换公用/私用、定时开关
  • 5(骰娘广播):生成warning

文件位置表

Mod文件示例

{
    "mod":"塔罗",
    "author":"Shiki",
    "dice_build":564,
    "brief":"外置模块样例文档。未来版本指令时可见",
    "comment":"模块信息,该内容只在源文件可见。未来版本dice_build会作为当前版本是否可用的参照",
    "helpdoc":{
		"塔罗":"塔罗牌,由“TAROT”一词音译而来,被称为“大自然的奥秘库”。它是西方古老的占卜工具,中世纪起流行于欧洲,地位相当于中国的《周易》",
        "愚者正位":"憧憬自然的地方、毫无目的地前行、喜欢尝试挑战新鲜事物、四处流浪。美好的梦想。",
        "愚者逆位":"冒险的行动,追求可能性,重视梦想,无视物质的损失,离开家园,过于信赖别人,为出外旅行而烦恼。心情空虚、轻率的恋情、无法长久持续的融洽感、不安的爱情的旅程、对婚姻感到束缚、彼此忽冷忽热、不顾众人反对坠入爱河、为恋人的负心所伤、感情不专一。",
        "魔术师正位":"事情的开始,行动的改变,熟练的技术及技巧,贯彻我的意志,运用自然的力量来达到野心。",
        "魔术师逆位":"意志力薄弱,起头难,走入错误的方向,知识不足,被骗和失败。",
        "女祭司正位":"开发出内在的神秘潜力,前途将有所变化的预言,深刻地思考,敏锐的洞察力,准确的直觉。",
        "女祭司逆位":"过于洁癖,无知,贪心,目光短浅,自尊心过高,偏差的判断,有勇无谋,自命不凡。",
        "女皇正位":"幸福,成功,收获,无忧无虑,圆满的家庭生活,良好的环境,美貌,艺术,与大自然接触,愉快的旅行,休闲。",
        "女皇逆位":"不活泼,缺乏上进心,散漫的生活习惯,无法解决的事情,不能看到成果,担于享乐,环境险恶,与家人发生纠纷。",
        "皇帝正位":"光荣,权力,胜利,握有领导权,坚强的意志,达成目标,父亲的责任,精神上的孤单。",
        "皇帝逆位":"幼稚,无力,独裁,撒娇任性,平凡,没有自信,行动力不足,意志薄弱,被支配。",
        "教皇正位":"援助,同情,宽宏大量,可信任的人给予的劝告,良好的商量对象,得到精神上的满足,遵守规则,志愿者。",
        "教皇逆位":"错误的讯息,恶意的规劝,上当,援助被中断,愿望无法达成,被人利用,被放弃。",
        "恋人正位":"撮合,爱情,流行,兴趣,充满希望的未来,魅力,增加朋友。",
        "恋人逆位":"禁不起诱惑,纵欲过度,反覆无常,友情变淡,厌倦,争吵,华丽的打扮,优柔寡断。",
        "战车正位":"努力而获得成功,胜利,克服障碍,行动力,自立,尝试,自我主张,年轻男子,交通工具,旅行运大吉。",
        "战车逆位":"争论失败,发生纠纷,阻滞,违返规则,诉诸暴力,顽固的男子,突然的失败,不良少年,挫折和自私自利。",
        "力量正位":"大胆的行动,有勇气的决断,新发展,大转机,异动,以意志力战胜困难,健壮的女人。",
        "力量逆位":"胆小,输给强者,经不起诱惑,屈服在权威与常识之下,没有实践便告放弃,虚荣,懦弱,没有耐性。",
        "隐者正位":"隐藏的事实,个别的行动,倾听他人的意见,享受孤独,有益的警戒,年长者,避开危险,祖父,乡间生活。",
        "隐者逆位":"憎恨孤独,自卑,担心,幼稚思想,过于慎重导致失败,偏差,不宜旅行。",
        "命运之轮正位":"关键性的事件,有新的机会,因的潮流,环境的变化,幸运的开端,状况好转,问题解决,幸运之神降临。",
        "命运之轮逆位":"挫折,计划泡汤,障碍,无法修正方向,往坏处发展,恶性循环,中断。",
        "正义正位":"公正、中立、诚实、心胸坦荡、表里如一、身兼二职、追求合理化、协调者、与法律有关、光明正大的交往、感情和睦。",
        "正义逆位":"失衡、偏见、纷扰、诉讼、独断专行、问心有愧、无法两全、表里不一、男女性格不合、情感波折、无视社会道德的恋情。",
        "倒吊人正位":"接受考验、行动受限、牺牲、不畏艰辛、不受利诱、有失必有得、吸取经验教训、浴火重生、广泛学习、奉献的爱。",
        "倒吊人逆位":"无谓的牺牲、骨折、厄运、不够努力、处于劣势、任性、利己主义者、缺乏耐心、受惩罚、逃避爱情、没有结果的恋情。",
        "死神正位":"失败、接近毁灭、生病、失业、维持停滞状态、持续的损害、交易停止、枯燥的生活、别离、重新开始、双方有很深的鸿沟、恋情终止。",
        "死神逆位":"抱有一线希望、起死回生、回心转意、摆脱低迷状态、挽回名誉、身体康复、突然改变计划、逃避现实、斩断情丝、与旧情人相逢。",
        "节制正位":"单纯、调整、平顺、互惠互利、好感转为爱意、纯爱、深爱。",
        "节制逆位":"消耗、下降、疲劳、损失、不安、不融洽、爱情的配合度不佳。",
        "恶魔正位":"被束缚、堕落、生病、恶意、屈服、欲望的俘虏、不可抗拒的诱惑、颓废的生活、举债度日、不可告人的秘密、私密恋情。",
        "恶魔逆位":"逃离拘束、解除困扰、治愈病痛、告别过去、暂停、别离、拒绝诱惑、舍弃私欲、别离时刻、爱恨交加的恋情。",
        "塔正位":"破产、逆境、被开除、急病、致命的打击、巨大的变动、受牵连、信念崩溃、玩火自焚、纷扰不断、突然分离,破灭的爱。",
        "塔逆位":"困境、内讧、紧迫的状态、状况不佳、趋于稳定、骄傲自大将付出代价、背水一战、分离的预感、爱情危机。",
        "星星正位":"前途光明、充满希望、想象力、创造力、幻想、满足愿望、水准提高、理想的对象、美好的恋情。",
        "星星逆位":"挫折、失望、好高骛远、异想天开、仓皇失措、事与愿违、工作不顺心、情况悲观、秘密恋情、缺少爱的生活。",
        "月亮正位":"不安、迷惑、动摇、谎言、欺骗、鬼迷心窍、动荡的爱、三角关系。",
        "月亮逆位":"逃脱骗局、解除误会、状况好转、预知危险、等待、正视爱情的裂缝。",
        "太阳正位":"活跃、丰富的生命力、充满生机、精力充沛、工作顺利、贵人相助、幸福的婚姻、健康的交际。",
        "太阳逆位":"消沉、体力不佳、缺乏连续性、意气消沉、生活不安、人际关系不好、感情波动、离婚。",
        "审判正位":"复活的喜悦、康复、坦白、好消息、好运气、初露锋芒、复苏的爱、重逢、爱的奇迹。",
        "审判逆位":"一蹶不振、幻灭、隐瞒、坏消息、无法决定、缺少目标、没有进展、消除、恋恋不舍。",
        "世界正位":"完成、成功、完美无缺、连续不断、精神亢奋、拥有毕生奋斗的目标、完成使命、幸运降临、快乐的结束、模范情侣。",
        "世界逆位":"未完成、失败、准备不足、盲目接受、一时不顺利、半途而废、精神颓废、饱和状态、合谋、态度不够融洽、感情受挫。"
    }
}

更新历史

更新说明(2.5.2)

  • 支持自定义文本使用sample转义多选一
  • [自动清理过期用户数据](#过期数据自动清理(.admin InactiveUserLine/InactiveGroupLine=360))(默认360天)
  • 高频指令通知将显示最后一次指令的内容
  • 增加入群后对同系Dice!的识别
  • 降低jrrp失败向后台报错的频率
  • 修复单次清群上限无作用的bug
  • 修复st格式错误导致CPU100%的恶性bug
  • 修复了一些其他bug

更新说明(2.5.1)

  • 优化{pc}丢失、群昵称获取失败等问题
  • 扩展通知窗口级别,允许.send notice
  • 允许自定义定时任务
  • 优化lua跨平台编码问题
  • lua预设函数新增到18个

更新说明(2.5.0)

  • 允许安装lua脚本指令
  • 本体独立于框架,必须由Dice驱动器装载
  • 使用.admin ListenSelfEcho/ListenGroupEcho自定义是否接收自己的私聊/群聊消息
  • 补充了对自身的指令频度监控,避免自我响应
  • 使用.admin GroupInvalidSize自定义协议无效的群规模
  • 开启对同系Dice的识别
  • 缓存今日人品,允许lua调用
  • 默认自动保存间隔调整为5分钟

更新说明(2.4.1)

  • 更新适配先驱框架的更新和重载功能,及其他接口适配
  • 新增.log日志记录功能
  • 新增.help模糊查询建议
  • 新增敏感词检测机制
  • 新增.groups查群功能
  • 优化.link功能
  • 视入群邀请者拥有群权限

更新说明(2.4.0)

  • 新增GUI,入口为酷Q【菜单】->【应用】->【Dice!】->【综合管理】

  • Dice!对Mirai适配

  • 新增统计信息

    系统新增硬盘占用检测。入群新增用户比例检测。新增每日用户、指令量统计。

  • 新增定时任务

    取消默认的每日保存,默认每十分钟保存一次。允许定时清理image/,默认关闭。

  • 优化空参数指令的响应

    当新人只知道指令不知道参数,在裸发".pc"时将难以得到有效反馈。为了提高交互效率,对于空参数无意义的指令,回执将重定向为对应的help文档。现在发送".pc"将收到与".help pc"相同的应答。与此同时一些原本用于空参数的回执条目被弃用,如:strStErr。

  • 新增牌堆格式

    在牌堆内可使用"::牌数::内容"的格式替换重复的项目。

  • 优化不良记录同步系统

    通过将其他来源的不良记录BlackList.json重命名为BlackListEx.json,放置于[DiceData]/conf目录下。Dice!加载时会自动读取并更新记录,之后该文件会被销毁。

    通过指令.cloud black可以读取云端经二次确认的记录

    新增.user diss 私黑指令

  • 新增group 协议无效词条

    类似于忽略,但允许!authorize和!warning以及来自admin的!dismiss。进入500人以上群时自动添加该词条,该规模的群聊不该认为自动建立用户协议。

  • 新增分页发送机制

    识别待发送消息中的'\f'后会将消息分段发送。

更新说明(2.3.8exp)

2.3.8Exp10:增设用户和群记录,将白名单转换为用户信任/群选项,自定义优化如有自定义strNameGenerator请参照格式及时重定义,以免无法显示 2.3.8Exp9:拦截消息设置。多轮检定将调用一组额外的strSuccess文本以避免糟糕的排版,如有需要请额外设置 2.3.8Exp8:新增warning,完善黑名单,开放设置功能 2.3.8Exp7:新增Admin、monitor,所有admin指令对master而言都可以用.master代替。为.en/.sc未输入属性值时分别设定了独立回执 2.3.8.6i:拆分.send两个发送方向的回执,自定义化掷骰检定的回执 2.3.8.5i:增加.send功能 2.3.8.2i:允许定时开关,设定了非master使用专属指令时的回复文本,设置strDismiss后将会在被.dismiss时回复 2.3.8.1i:设定了默认入群介绍

后记

更新手记(2022.11.21)

维护一个跨平台项目还是有些为难一个半路出家的程序员了,七月份被非Android环境的编译失败卡得死死的,最后还是溯洄出手解决的。好在每次溯洄跑路都只是把我逼出舒适区一点点,出大问题还会回来托底,让我不至于夭折的前提下成长。

Dice!的适配框架又转了几轮,mirai与gocq这两个开源框架基本可以固定了,源码流出后的MyQQ如有余力也可一试,只要tx那边没有大动作,框架这部分应该可以稳定下来了。希望未来可以调整好重心,挽救一下自己不堪入目的项目管理水平。

更新手记(2021.2.20)

溯洄又跑路了,结果2.5.0还没发布就又回来了。原本这一次跑路的影响比以往任何一次都要大,因为溯洄为先驱写了CQXQ,为MiraiNative更新了1.8.6,为手机模拟器更新了ExaGear,而以上项目,我 全 不 会。所以感谢风荷帮我搞定了Dice驱动器先驱插件的部分。我和溯洄都是主见极强的人,但我想我们都还在成长。

在酷Q时代,把一个插件拆成两个文件看起来很蠢,因为大大降低了安装和更新的便利性,提高了新人门槛。但现在无所谓了——无论CQXQ+Dice!还是MiraiNative+Dice!,转换插件已然不可避免。因此我有了DiceDriver+Dice!的想法,也顺便解决一个问题——心跳报告和不良记录上传的源码对所有人暴露导致的安全风险,现在这部分内容已经转移到DiceDriver中了。但是很可惜Mirai用户将不能不使用MiraiNative+DiceDriver+Dice!的配置,DiceDriver在这里只能充当酷Q插件转接Dice!。因为当我有这想法的时候,我甚至得从写mirai插件要用什么IDE开始问溯洄。

Dice!的第四个框架,我最终还是倾向于小栗子——它有设置头衔的api(不是)。

更新手记(2020.10.2)

2.4.1的阶段并没有像之前一样想着憋大招,一边适配先驱插件,一边加了些小功能。顺便也是在给自己的瓶颈拖时间,但是并没有什么实质性进展。

大概描述一下遇到的一些瓶颈:

pc卡格式的优化,现在的卡和卡模板稍显繁琐,但还是不方便录入文本值。需要敲定不同类型属性的存储格式后,才能做与云端的上传下载。

为更丰富的参数安排保留字符,如检定时临时调用指定pc的属性(.rc [pc1]/[skill1]),连接符号最好跨越中英文全半角,而塔骰使用的减号在Dice!中已经用作属性减值了。

自定义函数的格式,预想是{?like?1605271653&10?}表示点赞10次QQ1605271653。但是还要设计字符转义来保证完备性就很烦。

模块(mod)的安装卸载和优先级调整,以及把每个牌堆文件视为单独模块。当然这个不算瓶颈,只是一直没下手。

更新手记(2020.7.29)

这里是Shiki。我也没想到,exp11没了~~,饼不算数了~~。再回归成为Dice!主支的开发者,确实是我不曾想到的。当然这对用户而言没多大区别。作为一个整合过渡用的版本,这一说可以说没有什么明显的改动,尤其是在用户体验上。

当然又有新坑埋下了,那就是新的定时任务系统。我对造轮子有种额外的热衷,相较而言,新功能更像是机制建立后的水到渠成。~~所以我的饼做的比画的慢太多。~~等定时系统完善后,大概就可以用来跑团杀鸽子了。

更新手记(2020.5.14)

这里是Shiki,在半年时间里,exp10经历了一个繁复拖沓的开发周期。虽然有部分现实因素,但更多受限于开发者的项目管理能力不足。为了一次性将所有存档文件从酷Q默认文件夹搬迁到[DiceData]/,开发者被迫将所有认为需要改进的涉及文件的机制更新了一遍。好在有众多用户的积极支持,Dice!exp终于迎来了新一版正式发布版。

exp10的更新体现了两极分化的开发思路:对用户的极简化和对深度用户的高度定制化。对于开包即用的用户(超过Shiki全部万千骰娘的半数),初始设定将更贴合用户体验。对于没有认主导致的接收信息量不足问题,无主骰娘现在会在初始化后向自己发送通知,此后所有通知都会发送给自己。如此或有助于非官方渠道接触骰娘的用户更容易接触到骰娘的常规用法。

可以看到,exp10对黑白名单的扩展大大增加了用户的学习成本。因此我全套向下兼容了旧有的黑白名单、管理、监视窗口指令,试图构建简洁功能-高级功能并存的模式。用户可以不理解信任等级,而像之前那样使用.admin whiteqq和.master admin,也可以不理解单群设置而简单实用.admin whitegroup。

经历了exp9挖坑牌堆exp10完善后,我体验到了在大版本更新前挖好坑的模式,即提前做好扩展的空间等着大版本填坑。鉴于整个开发过于耗时,又希望生态圈内的创作者能尽早熟悉文件格式进行开发,exp10先用一天的开发时间支持了mod文件的基本格式,等待后续填充helpdoc以外的类型。session、mod在exp10都是看起来没那么重要的更新,但它们的作用都可以期待一下。那么,尽情催更exp11的开发吧,说不定到那时候,log、随机回复、敏感词检测、模糊匹配回复……都实现了呢。

Dice! 用户手册

本手册对应Dice!2.6.5版本,为用户指令提供说明。管理权限使用的指令请参考Master手册点此下载本手册_images/demo_bot.jpg

[TOC]

注意事项

  • 实际指令不需要加任何引号、括号,且以点号(.。!!)开头。 [参数]指代需要输入的参数,(可选)指代视情况可忽略,当前版本[]仅在st录入掷骰表达式时有意义。
  • 除前缀的指令标识符外,其余标点符号均不能用中文全角代替英文半角
  • 现在指令刷屏会导致拉黑,请善用多轮鉴定/复数生成指令,注意警告以及时停止。请用.rc 3#手枪代替3次.rc 手枪,用.r3#d10.rd10,用.coc 5 代替5次.coc
  • 指令频度达到第三阈值才会自动拉黑,但只要触发第一、第二阈值就会警告并提醒admin
  • 为了频度的平滑化,刷屏统计存在时间滞后,触发警告后,请冷却半分钟到一分钟

裸指令和指名指令

  • 停用状态:在群内使用.bot on/off调节,停用状态仅响应!authorize/!warning/.dismiss/.master/.bot指令

  • 裸指令:直接以.(。!!)开头发送消息,群内所有非停用状态的骰子都会响应该指令。

  • 指名:在指令前@骰子,未指名的骰子将不会响应指令;被指名的骰子如果未关闭DisabledListenAt选项,即使在停用状态也会响应指令;需要对复数骰子发送指令时,可以"@骰子1@骰子2[...@骰子n].指令";QQ回复消息本身视为开头带一个@

    _images/demo_at_order.jpg

.help 帮助文档

  • .help 查看帮助(strHlpMsg)
  • .help [词条名] 查看对应帮助文档

_images/demo_help_donate.jpg

*当词库中不存在同名条目时,会建议最接近的条目

_images/demo_help_match.jpg

.bot on/off 指令开关(群聊使用)

用法:(@骰子).bot (on/off)([骰子QQ或末四位])

  • .bot //群里所有骰子都会应答
  • .bot on 1605271653 //开启指定骰子
  • .bot off 8350 //输入QQ末四位与QQ号等效 //群中只有管理员有权限开关骰子
    //.bot无视静默状态,只要插件开启且不在内置黑名单总是有效
    可使用.help 指令查看指令列表。

.reply on/off 关键词回复开关(群聊使用)

用法:.reply (on/off)

回复是与指令并列的响应类型,由骰主自定义设置,可使用.help 回复列表查看回复关键词列表。

.dismiss 退群指令

默认协议所允许的将骰子送出群的指令,用于替代违反协议踢出骰子;只有管理员有权限送出骰子;.dismiss无视内置黑名单和静默状态,只要插件开启总是有效,除非群被忽略。

  • (群内)(@骰子).dismiss ([骰子QQ或末四位]) //在想要送出骰娘的群原地发送
  • (私聊).dismiss [要退出的群号]

!authorize 申请许可

向开启私用模式或审核模式、需要群【许可使用】的骰娘为群申请授权。受信任用户使用时将直接授权成功,否则申请信息会发送给管理员。

  • !authorize (+[群号]) 申请用途:[ **理由** ] 我已了解Dice!基本用法,认真阅读并保证遵守{strSelfName}的用户协议,如需停用指令使用[ **请填入指令** ],用后使用[ **请填入指令** ]送出群 //就地为本群申请时可忽略群号。

.send 发送消息

  • .send [反馈给Master的信息] //将消息发送给Master

.rules 规则速查

用法:.rules ([规则]):[待查词条] 或.rules set [规则]

  • .rules 跳跃 //复数规则有相同词条时,择一返回
  • .rules COC:大失败 //coc默认搜寻coc7的词条
  • .rules dnd:语言
  • .rules set dnd //设置后优先查询dnd同名词条
  • .rules set //清空默认规则,一般会先查询coc词条

.r 普通掷骰指令

用法:.r [掷骰表达式] ([掷骰原因]) 或.r [掷骰原因] [掷骰表达式]:([掷骰次数]#)[骰子个数]d[骰子面数](b[奖励骰个数])(p[惩罚骰个数])(k[取点数最大的骰子数])

  • .r //骰子面数默认100,可通过.set修改默认值
  • .r 沙漠之鹰 //存在录入角色卡时,可调用角色卡中保存的表达式
  • .r 1d4+2 中型刀伤害 //个数范围1-100,面数范围1-1000,否则非法
  • .r 3d6X5 幸运 //‘X’或'*'均视为乘号
  • .r 3#1d6 3发.22伤害 //每次结果分开发送
  • .r 1d10# 乌波·萨斯拉的子嗣 //掷骰次数范围1-10
  • .r3d6k2 //取点数最大的2个骰子
  • .r3#p 手枪连射 //奖惩骰固定为一个百面骰,不能与h以外其他字母共存
  • .rb2 瞄准后偷袭 //2个奖励骰
  • .rh 心理学 //暗骰,结果通过私聊发送
  • .rs1D10+1D6+3 沙鹰伤害 //省略单个骰子的点数,直接给结果 //现版本开头的r不再可用o或d代替 //一次掷骰超过20个将会自动排序

.ob 旁观模式

用法:.ob (exit/list/clr/on/off)

  • .ob join //加入旁观可以看到他人暗骰结果
  • .ob exit //退出旁观模式
  • .ob list //查看群内旁观者
  • .ob clr //清除所有旁观者
  • .ob on //全群允许旁观模式
  • .ob off //禁用旁观模式 //暗骰与旁观仅在群聊中有效

.set 设置默认骰

用法:.set [默认骰子面数]。如果已绑定角色卡,将修改角色卡的默认骰,否则修改用户配置的默认骰。

  • .set 20 //合法参数为1-1000
  • .set //重置为默认(COC7角色卡默认100)

.name 随机昵称

用法:.name (cn/jp/en)([生成数量])

  • .name 10 //默认三类名称随机生成
  • .name en //从指定子牌堆随机生成,后接cn/jp/en则限定生成中文/日文/英文名

.nn 设置称呼

用法:.nn [昵称] / .nn / .nnn (cn/jp/en)

  • .nn kp //掷骰时昵称前的./!等符号会被自动忽略
  • .nn del //删除当前窗口称呼
  • .nn clr //删除在各个群记录的所有称呼
  • .nnn //从随机姓名牌堆设置随机称呼
  • .nnn jp //从指定子牌堆随机昵称 //私聊时设置的昵称视为全局昵称 //该称呼用于{nick}的显示,优先级:群内称呼>全局称呼>群名片>QQ昵称;无角色卡或未命名时也用于显示{pc}

.coc COC人物作成

用法:.coc([7/6])(d)([生成数量])

  • .coc 10 //默认生成7版人物
  • .coc6d //接d为详细作成,一次只能作成一个 //仅用作骰点法人物作成,可应用变体规则,参考.rules创建调查员的其他选项

.dnd DND人物作成

用法:.dnd([生成数量])

  • .dnd 5 //仅作参考,可自行应用变体规则

.pc 多角色卡

所有群默认使用同一张初始COC7卡,如要在不同群使用不同卡游戏,需要在群内单独绑卡。每名用户最多可同时保存16张角色卡。

  • .pc new ([模板]:([生成参数]:))([卡名]) //新建空白卡。完全省略参数将生成一张COC7模板的随机姓名卡
  • .pc tag ([卡名]) //为当前群绑定指定卡,为空则解绑使用默认卡。私聊使用将绑定全局默认卡。
  • .pc show ([卡名]) //展示指定卡所有记录的属性,为空则展示当前卡
  • .pc nn [新卡名] //重命名当前卡,不允许重名
  • .pc cpy [卡名1]=[卡名2] //将后者属性复制给前者,前者不存在则新建卡
  • .pc del [卡名] //删除指定卡
  • .pc list //列出全部角色卡
  • .pc grp //列出各群绑定卡
  • .pc build ([生成参数]:)(卡名) //根据模板填充生成属性(COC7为9项主属性)
  • .pc redo ([生成参数]:)(卡名) //清空原有属性后重新生成
  • .pc clr //销毁全部角色卡记录
  • .pc stat //查看角色掷骰统计

.st 属性录入

用法:.st (del/clr/show) \[属性名] [属性值] //将属性录入当前绑定卡 .st力量:50 体质:55 体型:65 敏捷:45 外貌:70 智力:75 意志:35 教育:65 幸运:75

  • .st hp-1 //+/-开头时,视为基于原值修改
  • .st san+1D6
  • .st &沙漠之鹰=1D10+1D6+3 以&开头录入掷骰表达式,可被掷骰指令直接调用
  • .st del kp裁决 //删除已保存的属性
  • .st clr //清空人物卡
  • .st show 灵感 //查看指定人物属性
  • .st show //无参数时查看所有属性(不含默认值技能),请使用只st加点过技能的半自动人物卡! //部分COC属性会被视为同义词,如智力/灵感、理智/san、侦查/侦察

.ra/rc 检定指令

用法:.ra/rc ([检定轮数]#)[属性名] ([成功率]) //角色卡设置了属性时,可省略成功率

  • .rc 困难智力 99 //困难、极难在技能名开头视为关键词
  • .rc 自动成功爆破 //自动成功在技能名开头视为关键词,非大失败即成功
  • .rc体质*5 //允许使用+-*/,但顺序要求为乘法>加减>除法
  • .rc 敏捷-10 //修正后成功率必须在1-1000内
  • .rc3#p 手枪 //轮数与奖惩骰至多9个 //默认使用规则书检定 //使用.setcoc以设置默认房规

.setcoc 设置默认房规

用法:.setcoc [房规] //为当前群设置检定房规

  • .setcoc 2 当前可以设置房规1-5

  • .setcoc show 查看当前房规

  • .setcoc clr 清除房规(调用骰娘设置的默认规则) 0(规则书) 出1大成功 不满50出96-100大失败,满50出100大失败 1 不满50出1大成功,满50出1-5大成功 不满50出96-100大失败,满50出100大失败 2 出1-5且<=成功率大成功 出100或出96-99且>成功率大失败 3 出1-5大成功 出96-100大失败 4 出1-5且<=成功率/10大成功 不满50出>=96+成功率/10大失败,满50出100大失败 5 出1-2且<成功率/5大成功 不满50出96-100大失败,满50出99-100大失败

    6

    个位数=十位数且<=成功率则大成功

    个位数=十位数且>成功率则大失败

    房规2与3的区别在于:初始值为1的技能在②下大成功率只有1,而在③下有5

## .log 日志记录

  • .log new 日志名 另开日志并开始记录
  • .log on 继续记录
  • .log off 暂停记录
  • .log end 完成记录并发送日志文件 日志名须作为文件名合法,省略则使用创建时间戳。上传有失败风险,届时请.send骰娘后台索取

.sc 理智检定

用法:.sc[成功损失]/[失败损失] ([当前san值]) //已经.st了理智/san时,可省略最后的参数

  • .sc0/1 70
  • .sc1d10/1d100 直面外神 //当调用角色卡san时,san会自动更新为sc后的剩余值 //程序上可以损失负数的san,也就是可以用.sc-1d6/-1d6来回复san,但请避免这种奇怪操作 //大失败自动失去最大san值,大失败由setcoc判定

.ti/li 疯狂症状

  • .ti 临时疯狂症状
  • .li 总结疯狂症状 //适用coc7版规则,6版请自行用百面骰配合查表

.en 成长检定

COC规则,用法见.rules 用法:.en [技能名称]([技能值]) //已经.st时,可省略最后的参数,调用人物卡属性时,成长后的值会自动更新

  • .en 教育 60 教育增强 //
  • .en 幸运 +1D3/1D10 幸运成长 //Pulp规则中的幸运成长

.ri 先攻掷骰

先攻仅在群聊中有效,自动生成先攻值并从大到小排序 用法:.ri([加值])([角色名])或.ri([表达式])([角色名])

  • .ri //默认掷骰D20用作DND先攻
  • .ri -1 某pc //自动记入先攻列表
  • .ri +5 boss //当参数不以数字或加减号开头时,视为使用自定义表达式
  • .ri2DK //允许两次取大的情况
  • .ri 80 怪物甲 //只利用先攻排序功能的变体用法
  • .init //查看先攻列表
  • .init clr //清除先攻列表

.w 骰池

用法:.w(w) [骰子个数]a[加骰参数]

  • .ww 敏捷+剑 //掷骰表达式将自动调用角色卡存储的属性 ¶ //.w会直接给出结果而.ww会给出每个骰子的点数 //固定10面骰,每有一个骰子点数达到加骰参数,则加骰一次,最后计算点数达到8的骰子数 //具体用法请参考相关游戏规则

.me 第三人称动作

用法:.me ([群号]) [动作]

  • .me 笑出了声 //仅限群聊使用
  • .me 941980833 抱群主 //仅限私聊使用,此命令可伪装成骰子在群里说话
  • .me off //群内禁用me
  • .me on //群内启用me

.jrrp 今日人品

  • .jrrp //今日的人品值(1-100),每日更换
  • .jrrp off //群内禁用jrrp
  • .jrrp on //群内启用jrrp 2.3.5版本后随机值为均匀分布, 骰娘只负责从服务器搬运结果,请勿无能狂怒 如何发配所有人的命运,只有孕育万千骰娘生机之母,萌妹吃鱼之神,正五棱双角锥体对的监护人,一切诡秘的窥见者,时空舞台外的逆流者,永转的命运之轮本尊掌握

.welcome 欢迎词

用法:.welcome [欢迎词] //每有新人入群时将发送欢迎词

.welcome {at}欢迎{nick}入群~ //设置欢迎词 //{at}视为at入群者,{nick}会替换为新人的昵称,更多转义见手册 .welcome clr //清空欢迎词 .welcome show //查看欢迎词 无论指令是否停用,只要有欢迎词时有人入群,都会响应

欢迎词可用转义

  • help帮助词条

    {help:指令}

  • sample随机取一

    {sample:1|2}

  • case条件差分

    {case:差分变量?条件1=值1&条件2=2&else=备选值}

    例:{case:user.gender?male=男&female=女&else=未知性别}

.group 群管理操作(仅群管理可用)

用法:群内.group \[opt]或私聊.group \[群号] \[opt](两种形式等效)

  • .group state //查看在本群的设置及群员信息
  • .group diver //列出潜水最深的群员 //仅群管理可用
  • .group pause //群内全局禁言
  • .group restart //群内全局禁言解除
  • .group +/-[群管词条] //为群加减设置 群管词条:停用指令/禁用回复/禁用jrrp/禁用draw/禁用me/禁用help/禁用ob/拦截消息/
  • .group card [对象QQ/at对象] [群名片] //设置群名片。注意名片前的空格 //仅骰主可用
  • .group ban [at群员/群员QQ] [时长表达式(分钟)] //禁言群员,时长小于0时视为解除禁言
  • .group kick [at群员/群员QQ](...+[群员]) //批量移出群员
  • .group title [授衔对象QQ/at对象] [群头衔] //(仅对群主可用)设置群头衔。注意头衔前的空格

.draw 抽牌

用法:.draw [牌堆名称] ([抽牌数量]) //不放回地抽牌;抽牌数量不能超过实例规模

  • .draw _[牌堆名称] //暗抽,回复私聊并抄送旁观者
  • .drawh [牌堆名称] //暗抽,参数h后必须留空格 例:.draw 调查员背景 牌堆名称优先调用牌堆实例,如未设置则从同名公共牌堆生成临时实例 抽到的牌不放回,实例抽空后无法继续 骰子可以自行配置扩展牌堆,要了解不同骰子配置的具体牌堆,请.help 全牌堆列表或.help 扩展牌堆

.deck 牌堆

该指令可以在群内自设牌堆,使用.draw时,牌堆实例优先级高于同名公共对象。从牌堆实例抽牌不会放回直到抽空。每个群的牌堆列表至多保存10个牌堆

  • .deck set ([牌堆实例名]=)[公共牌堆名] //从公共牌堆创建实例

  • .deck set ([牌堆实例名]=)member //从群成员列表创建实例

  • .deck set ([牌堆实例名]=)range [下限] [上限] //创建等差数列作为实例

  • .deck show //查看牌堆实例列表

  • .deck show [牌堆名] //查看剩余卡牌

  • .deck reset [牌堆名] //重置剩余卡牌

  • .deck clr //清空所有实例

  • .deck new [牌堆名]=\[卡面1](...|[卡面n]) //自定义牌堆 例:

    .deck new 俄罗斯轮盘=有弹|无弹|无弹|无弹|无弹|无弹 除show外其他群内操作需要用户信任或管理权限

.ak 安价安科

.ak#[标题].ak new [标题] 新建分歧并设置标题(可为空) .ak+[选项].ak add [选项] 为本轮分歧添加新选项,用|分隔可一次添加多个选项 .ak-[选项序号].ak del [选项序号] 移除指定序号的选项 .ak=.ak get 均等随机抽取一个选项并结束本轮分歧 .ak show 查看分歧选项 .ak clr 清除本轮分歧

扩展指令

说明

以下指令并非Dice!内置指令,但已作为lua脚本向所有骰主开放下载,可在Dice!论坛或群内找到相关资源。如有未涉及的掷骰规则也可申请定制。

.rdc D20检定

.rdc(B/P) (+/-[加值]) ([检定理由]) ([成功阈值]) 参数[优/劣势骰]:可选,B=2D20取大,P=2D20取小 参数[加值]:可选,加值最后修正到D20结果上 参数[检定理由]:可选,将显示在回执语句中 参数[成功阈值]:可选,将与掷骰结果比较,返回成功或失败 空参返回本段说明文字

.rdx 双重十字(DoubleCross,DX)

.rdx[骰数]c[暴击值] 结果取[骰数]个D10的最高点数,暴击则重骰并累加点数 暴击值可提前设定(如.st暴击值8),默认10 重骰超过30次将强行终止

.rsr 暗影狂奔(Shadowrun)

.rsr[骰数] 出5/6视为命中 出1过半视为失误

.rnc 永夜后日谈(永い后日谈のネクロニカ)

D10攻击判定决定命中部位

.rfc 祸不单行(Fiasco)

.rfc bXwY 掷XD6白骰+YD6黑骰,相消取颜色和结果

.rbh 山屋惊魂(Betrayal at house on the hill, 小黑屋)

.rbh[骰数]

每粒骰子为骰面0或1或2的三面骰

.rws 武术(Wushu)

武术X(取S) //XD6求不超过S的骰数 武术X阴Y阳(取S) //X阴骰Y阳骰分别求不超过S的骰数 一个D6骰面不超过成功等级S视为一次命中 成功等级S由PC与判定最相关的特性决定,默认为2 骰池数X由动作扮演程度决定,一般为1~5,戏剧场景可以有6~8,至少为1 骰池中阴骰与阳骰数由玩家分配

常见问题

  • 加骰娘好友没被同意也没被拒绝?

    可能原因:骰娘关闭了ListenFriendRequest,不由Dice!监听好友申请;申请人账号因【加好友过于频繁】等理由被屏蔽好友申请

    解决方案:通知骰主手动处理请求,被屏蔽的申请需要点开【过滤通知】查看

  • 骰娘只在特定群对指令不响应?

    可能原因:骰娘处于停用指令状态;群在骰娘黑名单中;群内全群禁言但骰娘不是管理;群内开启每分钟发言限制而骰娘达到限制且不是管理

    解决方案:若.bot on无果,则通知骰主

  • 骰娘在群聊内均不响应而私聊响应

    可能原因:骰娘被tx屏蔽;骰娘被tx判定违规禁止使用群聊

    解决方案:通过用户群等渠道提醒骰主

  • 骰娘离线

    原因:一个离线的账号自然是不可能有任何响应的

    解决方案:通过用户群等渠道询问骰主

  • QQ新拉起来的多人聊天没有群号?

    原因:QQ当前所有群聊类型是统一的,区别只在于资料界面的详略程度

    解决方案:.group state/.rh等指令都可以在回执中看到消息来源群号

使用实例

实例:使用Dice!跑COC团

一、确认检定房规,使用.setcoc群内设置

出于公开公平性考虑,群内房规不受用户私人设置影响,只能在群内为每个群分别.setcoc

二、(可选)使用.coc7 生成属性

如使用购点法生成调查员,该步骤可略过。例如kp规定roll十选一,则使用.coc7 10生成10条数据

三、在群内绑定参团pc角色卡

如果未录入过pc数据,则使用.pc new新建角色卡,如.pc new 卡特,之后使用.st指令录入数据。如果已存在参团角色卡,使用.pc tag绑定到跑团群使用。

四、进行游戏

游戏内可使用.rc进行技能检定,.sc进行理智检定,.en进行成长检定,使用形如.st hp-1的指令记录属性变化。

实例:使用Dice!跑DND团

一、使用.set设置默认骰

使用.set 20将默认骰设置为D20

二、(可选)使用.dnd 生成属性

如使用购点法生成pc,该步骤可略过;如允许属性互换也可自便。

三、在群内绑定参团pc角色卡

如果未录入过pc数据,则使用.pc new新建角色卡,如.pc new DND:Shadow,之后使用.st指令录入数据。如果已存在参团角色卡,使用.pc tag绑定到跑团群使用。Dice!不提供DND角色模板,可向确认骰主有无安装扩展

四、进行游戏

游戏内可使用.rd进行检定,.ri进行先攻检定,.init查看先攻列表,使用形如.st hp-1的指令记录属性变化。

实例:使用Dice!身份抽签(狼人杀)

一、选定GM,群内使用.ob进入旁观视角

进入旁观后可以收到群内所有暗骰结果,确保GM知晓每个人的身份

二、使用.deck设置身份牌堆

狼人杀需要每个人抽取不放回,因此需要.deck new在群内新建牌堆实例。可以用::[数目]::[卡面]的格式表示重名卡牌。例:.deck new 狼人杀=::3::村民|::3::狼人|预言家|女巫|猎人

三、玩家使用.draw抽取身份

Dice!支持.drawh 狼人杀.draw_狼人杀两种暗抽指令格式,注意前者空格不可省略。为保证骰娘能私聊每个玩家成功,建议所有玩家加骰娘好友。

四、(可选)玩家使用.ri先攻生成序号

原本.ri用于DND先攻检定,但其生成的有序列表可以用于编号,在所有玩家进行一次.ri后,使用.init list可查看列表。

五、开始你的游戏

六、使用.deck reset洗牌重复利用

只要玩家人数不变,原牌堆reset重新装填后即可再次使用。

Dice!嵌入模块开发手册

(原Shiki Plugin Manual)

简介

本手册是对Dice!2.4.2(build570)新增的自定义指令功能、Dice!2.5.1(build577)新增的自定义任务、Dice!2.6.0(build577)支持调用Lua的关键词回复、Dice!2.6.4(build612)支持的事件处理等所作的说明,当前对应最新版本Dice!2.7.0alpha6(644)。通过在mod目录安装扩展模块,在plugin目录放入lua/js/py脚本或toml配置文件,Dice!将监听特定消息或事件并做出响应,也可基于时刻或循环处理定时任务,从而实现骰主对骰娘的深度化定制。扩展模块旨在以下方面实现对Dice!内建功能的补充:

  1. 过于客制化而无法使用现有数据结构表现的功能,如:基于D20结果的数值分段回复;
  2. 同人性质或版本各异的规则,如:JOJO团、圣杯团、骑士团、方舟团等;
  3. 因受众过偏不适合写入骰娘源代码的规则;
  4. 非骰娘功能,如:好感度系统;

从零编写脚本需要对Dice所支持脚本语言(lua/javascript/python)的其中一种有一定的了解,建议使用VS Code等工具编辑脚本。如果实在脚本苦手,手册附录准备了现成的样例脚本,你也可以在Shiki官方群或论坛内找到Shiki免费发布共享的脚本。Shiki的桌游指令集已收录:DND检定、ShadowRun、双重十字、永夜后日谈(命中检定)、祸不单行、山屋惊魂。**如果你只要安装现成模块,只需记住在webui界面【模块管理】子标签【远程资源】下选择安装;或手动将mod文件放入mod文件夹后启动或.system load。**如果你实在有想实现的指令,没办法修改现成脚本实现,也确实找不到人白嫖的话,可以联系Shiki定制。愿所有人能从Dice!骰娘处获得更好的用户体验,拥有自己独一无二的骰娘。

Shiki的开发官方群:【一群】928626681【二群】1029435374

——安研色Shiki

版权声明

Dice!使用AGPLv3许可,如果你将Dice!与Lua脚本结合用于分发或通过网络提供服务,那么Dice!与所搭载脚本将作为整体使用AGPLv3许可,任何接收者或用户基于AGPLv3许可,均完全享有获取并公开完整代码的自由。

同样地,本手册使用AGPLv3许可,如果您通过付费方式获取到本手册或本手册附带的Lua脚本,那么您显然付出了不必要的代价。

特别地,若您将Dice!源代码从C++重写为其他语言,这种翻译行为属于著作权法意义上的修改,因此您翻译的代码一旦分发或用于网络交互,也强制使用AGPLv3协议且必须保留Dice!开发者的署名。AGPLv3文本格式参考:

--[[ 
Copyright (C) 2018-2022 w4123溯洄
Copyright (C) 2019-2023 String.Empty
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. 
]]

脚本语言嵌入说明

Dice!在更新过程中支持嵌入Lua(Dice!2.4.2)、Python(Dice!2.7.0)、JavaScript(Dice!2.7.0)脚本,包括执行语句或执行文件。

Lua嵌入方式

①指定脚本文件的指定函数名

最原始的形式,只能定义前缀匹配关键词的指令。

--/plugin/hello_world.lua
msg_order = {
    ["hello"] = "hello,world",
}

function (msg)
    return "hello,world"
end
②指定语句

常用于直接以.reply指令或webui设置回复:

.reply set Title = hello_world

Prefix=hello

Lua=return "hello,world"

#/mod/hello_lua/reply/hello_world.toml
#可以在toml文件内写入脚本语句但不推荐
[reply.hello]
type = "Order"
keyword.prefix = "hello"
echo.lua = """return 'hello,world'"""
--/mod/hello_lua/reply/hello_world.lua
--可以这么写但完全不推荐,请采用④
reply.hello = {
    type = "Order",
    echo = {
        lua = [[return "hello,world"]]
    },
}
③指定脚本文件

既可用于直接以.reply指令或webui设置回复,又可用于mod内设置:

.reply set Title = hello_world

Prefix=hello

Lua="hello_world"

#/mod/hello_lua/reply/hello_world.toml
[reply.hello]
type = "Order"
keyword.prefix = "hello"
echo.lua = "hello_world"
--/mod/hello_lua/script/hello_world.lua
return "hello,world"
④直接定义函数
--/mod/hello_lua/reply/hello_world.lua
reply.hello = {
    type = "Order",
    echo = {
        lua = function(msg)
            return "hello,world"
        end
    }
}

Lua嵌入特性

  • 每次lua脚本执行都在独立的虚拟机(lua_State)中,全局变量相互独立;
  • 每次新建虚拟机时将预置函数写入全局;
  • 由于整数自动转换为字符串时会保留一位小数,取账号ID或群号时多用字符串类型;
  • 执行关键词回复的lua脚本时,依嵌入方式视为在全局对象中写入msg后运行,或将msg作为参数传入后执行函数;运行返回值可文本化时,将作为消息回复;

JavaScript嵌入方式

①指定语句

常用于直接以.reply指令或webui设置回复:

.reply set Title = hello_world

Prefix=hello

Js="hello,world"

#/mod/hello_lua/reply/hello_world.toml
#可以在toml文件内写入脚本语句但不推荐
[reply.hello]
type = "Order"
keyword.prefix = "hello"
echo.js = """'hello,world'"""
②指定脚本文件

既可用于直接以.reply指令或webui设置回复,又可用于mod内设置:

.reply set Title = hello_world

Prefix=hello

Js="hello_world"

#/mod/hello_lua/reply/hello_world.toml
[reply.hello]
type = "Order"
keyword.prefix = "hello"
echo.js = "hello_world"
--/mod/hello_lua/script/hello_world.js
msg.echo("hello,world")

JavaScript嵌入特性

  • Dice!采用(修改后支持MSVC的)Quickjs实现JavaScript的嵌入;
  • 所有js脚本执行共用一个运行时(JSRuntime),每次的语境(JSContext)相互独立,全局变量相互独立;
  • 每次新建语境时将预置函数写入全局;
  • 执行关键词回复的js脚本时,视为在全局对象中写入msg后运行;

Python嵌入方式

①指定语句

常用于直接以.reply指令或webui设置回复:

.reply set Title = hello_world

Prefix=hello

Py=msg.echo("hello,world")

#/mod/hello_lua/reply/hello_world.toml
#可以在toml文件内写入脚本语句但不推荐
[reply.hello]
type = "Order"
keyword.prefix = "hello"
echo.py = """msg.echo('hello,world')"""
②指定脚本文件

既可用于直接以.reply指令或webui设置回复,又可用于mod内设置:

.reply set Title = hello_world

Prefix=hello

Py="hello_world"

#/mod/hello_lua/reply/hello_world.toml
[reply.hello]
type = "Order"
keyword.prefix = "hello"
echo.py = "hello_world"
--/mod/hello_lua/script/hello_world.py
msg.echo("hello,world")

Python嵌入特性

  • Dice!采用CPython实现Python的嵌入,需要外部安装有python3.10,如环境变量中不包含python,Windows环境可将python3.dllpython310.dllpython310.zip放入Dice根目录/bin文件夹下,然后设置Master配置项EnablePython=1并重启;
  • 所有py脚本执行共用一个虚拟机,需慎用全局变量
  • 全局初始化时以from dicemaid import *的方式写入预置函数;
  • 执行关键词回复的py脚本时,视为在本地变量中写入msg后运行;运行结果可文本化时,将作为消息回复;
  • 向用户数据中写入元表(Tuple)时,将视同列表(List)处理;

模块Mod

模块是Dice!外置可热插拔的扩展文件集,远程装卸、开关的单元。Mod采用类Paradox风格结构,由Dice目录/mod/下的json文件及其同名文件夹构成:mod_name.json写有mod的标题、作者、版本信息、说明文本等;同名mod_name目录下子目录存放不同类型文件。

阅读说明:下文中形如(build609+)的注释用于说明该特性在何时被添加或最后编辑,开发者应当在制作mod时,将dice_build设定为满足mod内所有特性要求的最小build。

[DiceData]
|-- mod
    |-- mod_name.json
    |-- mod_name
        |-- event
        |   |-- good_morning.lua
        |
        |-- reply
        |   |-- good_morning.lua
        |
        |-- script
        |   |-- event_good_morning.lua
        |   |-- reply_good_morning.lua
        |
        |-- speech
        |   |-- rlobal_msg.yaml
        |
        |-- image
        |-- audio

Mod主文件

{
    "title":"角色卡栏位扩展",
    "ver":"1.0.0",
    "author":"安研色Shiki",
    "dice_build":606,
    "brief":"角色卡栏位扩展(状态栏、物品栏、法术栏、专长栏、笔记栏等)",
    "require":[],
    "comment":"",
    "helpdoc":{
        "title":"模块名称",
        "ver":"mod版本号",
    	"author":"作者署名",
        "dice_build":"支持mod运行的最低Dice!版本号,低于此项的Dice!将放弃读取该mod",
    	"brief":"会在Dice!中展示的模块简介",
    	"require":"mod所需前置mod,若未满足则将放弃加载",
    	"comment":"不会写入,仅用作文件内的注释项",
        "helpdoc":"帮助文档,其中的项目可被.help获取"
    }
}

mod主文件读取成功后,Dice!将尝试进一步读取同名子目录*(build599+)*。

Mod子目录

event

以lua*(build609+)或toml(build636+)*形式向表event写入事件。load时读入,修改后需要system load应用。

lua格式
event.listen_friend_request = { --该事件的ID,唯一对应,同名覆盖
    title = "好友审核", --事件标题
    trigger = { --触发方式,无则只能直接通过ID调用
        hook = "FriendRequest"	--代理内置的好友申请事件
    },
    --调用script/listen_friend_request.lua
    action = { lua = "listen_friend_request" } 
}
event.good_morning = {    
    title = "早安",
    trigger = {
        clock = { --在每日指定时点触发,可设置多个时点
            {hour=7,minute=30}
        }
    },
    --调用script/daily/good_morning.lua
    action = { lua = "daily.good_morning" } 
}
event.heartbeat = {    
    title = "心跳",
    trigger = {
        cycle = { --计时循环,所有cycle事件在初始化后立刻执行第一次
            minute=5,second=30 --循环执行的间隔
        }
    },
    action = { lua = "heartbeat" } 
}
toml格式
[event.listen_friend_request]   
title = "好友审核"
trigger.hook = "FriendRequest"
action.lua = "listen_friend_request"

[event.good_morning]   
title = "早安"
trigger.clock = [{hour=7,minute=30}]
action.lua = "daily.good_morning"

[event.heartbeat]   
title = "心跳"
trigger.cycle = { minute=5,second=30 }
action.lua = "heartbeat"
reply

以lua*(build603+)或toml(build636+)*形式向表msg_reply(toml为reply)写入关键词回复。load时读入,修改后需要system load应用。

  • keyword: 触发该回复的关键词,可以有多种触发形式和多个关键词。
  • limit: 触发限制条件。table键值表示条件类型=条件内容
  • echo: 回复内容。值为文本时,回复该文本;值为数组时,作为牌堆回复;值为表且形如{lua=文件名}时,调用lua文件。
lua格式
msg_reply.good_morning = {	--该条msg_reply的id,唯一对应,同名覆盖
    keyword = {
        match = "早",
        prefix = {"早安","早上好"},
    },
    limit = {
        today = { user = 1 },	--每用户当日触发次数
    },
    echo = {
        lua = "reply_good_morning"	--调用文件名对应lua
    }
}
msg_reply.good_night = {
    keyword = {
        prefix = "晚安",
    },
    limit = {
        cd = { user = 60 },
        today = { user = 2 },
    },
    echo = "{reply_good_night}"	--直接回复文本
}
toml格式
[reply.good_morning]
keyword = { match = "早", prefix = ["早安","早上好"] }
limit.today.user = 1
echo.lua = "reply_good_morning"

[reply.good_night]
keyword.prefix = "晚安"
limit = { cd = { user = 60 }, today = { user = 2 }}
echo = "{reply_good_night}"
script

script目录中的lua文件名(不含后缀)可作为loadLua函数的参数,也可直接作为关键词回复Lua形式的回复内容。推荐script文件名及文件内不出现中文字符。

script中的文件不会被预加载,而是调用时实时读取,因此热更新后不需要使用.system load加载。

---reply_good_morning.lua
clock_now = now = os.date("*t")
if(now.hour>12)then
    msg.hour = now.hour
    return "{reply_good_morning_late}"
elseif(now.hour<5)then
    return "{reply_good_morning_early}"
else
    return "{reply_good_morning}"
end
speech

*(build603+)*台词speech是自定义回执文本的上位,可直接由花括号转义。每项条目可存多条文本,等效于单抽放回的牌堆或{sample}。load时读入,修改后需要system load应用。

reply_good_morning:
 - "早上好啊{nick}"
 - "那{nick}也早安哦"
reply_good_morning_late: "{hour}点的早安?{nick}起得可真早呢"
reply_good_morning_early: "早安,{nick}的睡眠还够吗?"
reply_good_night: 
 - "晚安呐"
 - "也祝{nick}晚安"
strRollDice: "{pc}掷骰:{res}"
strRollRegularSuccess: 
 - 成功了哦
 - "成功 看来{self}还是护佑着{nick}的"
image

*(build633+)*图片文件夹,会在mod加载时复制到/data/image文件夹中用于在消息中发送。

audio

*(build633+)*语音文件夹,会在mod加载时复制到/data/record文件夹中用于发送语音。

Lua快速教程

如果你已经了解过脚本语言lua,请跳过该部分.由于function使用小括号()输入参数,下文使用中括号[]表示参数可省略

Lua基本介绍

Lua是一种用标准C语言编写并开源的嵌入式脚本语言。

Lua语句不使用分号或其它标点作结尾,不使用大括号或缩进表示作用域,使用"end"作为函数、条件判断、while循环的结束。单行注释以"--"开头,与或非运算符使用"and""or""not"。Lua字符串与数组的首位索引均为1,而不是一般编程语言的0。

Lua标识符

  • 逻辑运算: 与and; 或or; 非not

  • 运算符号: 赋值=; 等号==; 不等号~=; 小于号< ; 小于等于号<=; 四则运算+-*/; 取余%; 乘幂^; 取整除法//; 连接.. ;

  • 取长度#: #table#string

  • 函数参数():function(args)

  • 表索引键值(index)[]: table[key]

  • 字符串(不可混用):"字符串"/'字符串'/[[不转义换行等符号的字符串]]

  • 单行注释 --

    -- lua单行注释
    
  • 多行注释 -[[]]

    --[[
    lua多行注释
    ]]
    

Lua数据类型

Lua中的变量一共有8种基本数据类型,具体类型在赋值时自动判断而不需要声明。编写Dice!脚本需要了解其中6种:空(nil)、逻辑型(boolean)、双精度浮点数(number)、字符串(string)、函数(function) 和 表(table)。使用type()函数可以返回一个表示变量类型的文本值,如type(msg)=="table"。

空值nil

nil类型只有nil一种值,表示无效值,比如一个没有赋值过的变量,访问表中不存在的key,缺失的函数返回值。函数不返回相当于返回nil,对变量赋值nil等于删除该变量。例:试图用string.match匹配字符串时,如果匹配失败,则只返回nil。nil不能视作空字符串参与运算,所以需要预先考虑变量为nil的情况。

逻辑值(布尔)boolean

boolean类型有两种值:真true,假false。所有变量都可以自动转换为逻辑值,非空变量均视为真,空变量(nil)视为假。

数值number

lua中所有的数都可视为双精度浮点数(double)。

string与number进行算术运算时(如"6"*5),若string可转换为数字,将进行自动类型转换,否则报错;string与number进行取相等运算时自动为false(如"123"~=123);string与number用不等号比较时必定报错(如"6">5

文本值(字符串)string

Lua中的string可以用三种方式表示:"双引号"、'单引号'、[[双层中括号]],其中双层中括号内引号、换行符等特殊字符不需要转义。string不能使用加减符号,可以通过".."连接。在string前加"#"表示该字符串的长度。

  • str1..str2

    连接字符串str1与str2,若连接到数字,将数字自动保留6位小数打印。

  • string.match(str, pattern[, init])

    从str的init位置起,寻找正则匹配pattern的子串,成功返回匹配到的string,失败返回nil。init默认为1。

    例:string.match(msg.fromMsg,"%d*",#order_name+1)从指令名的后一位开始,从消息中匹配数字。

  • string.len(str)

    返回str的长度,等效于#str。

  • string.format(...)

    将特定类型变量按格式转化为string。

    例: string.format("%.0f",die)将掷骰结果按保留0位小数的浮点数打印。

  • string.sub(str, init [, end])

    从str中截取从init位置到end的子串,end默认-1(截取到最后)

    例:string.sub(msg.fromMsg,#order_name+1)截取消息从指令名后一位开始的文本

  • string.find (str, substr, [init, [end]])

    在str从init到end的位置间从左至右寻找匹配substr的子串。如匹配成功,返回str中子串的首位,否则返回nil。

  • string.upper(str) 将str中所有字母全部大写。

  • string.lower(str) 将str中所有字母全部小写。

  • string.rep(str, n) 返回将str重复n遍后连接的string。

    例:string.rep("木大",8)返回"木大木大木大木大木大木大木大木大"。

表table

table是存储变量的容器(关联数组),可以通过不同的索引访问对应的值,格式为table[key]

当table的键值为纯ANSI字符时(非纯数字、无中文等多字节字符),可用'.'代替[""]格式,如msg.fromQQ等价于msg["fromQQ"],但不能用msg_order.抱Shiki来代替msg_order["抱Shiki"]

  • table.concat(tab[, sep])

    将表tab中所有元素作为string连接,使用sep分隔。

    例:table.concat(die_pool,'+')将所有骰目通过加号连接。

  • table.insert(tab, [pos, ]value)

    在table的pos位置插入value,默认插入在末尾。

    例:table.insert(die_pool,die)将本次骰目加入结果池中

  • table.sort (tab [, comp])

    将tab按comp函数进行排序,默认进行升序排序。

    例: table.sort(die_pool,function(a,b) return (a>b) end)当需要为掷骰结果取最大的若干个时,就需要预先为骰目降序排序。

--初始化空表
msg_order = {}
--向表中插入键值对
msg_order[".duel"] = mdice_duel
函数function

函数在定义时需要声明function 函数名(参数列表),使用return返回,使用end作为函数体的结尾。函数不需要规定返回值的类型和数量,甚至可以在不同的位置返回不同的值。返回多个值时,值之间以','分隔。不要在return的下一行接一般语句。

function max_min(a,b)
	if(a>b)then
		return a,b
	else
		return b,a
	end
end

Lua流程控制

if()then...elseif()then..else...end

if(语句为真)then [执行语句] end。可使用多个elseif()then来进行判定语句为假时的后续判定。

注意:elseif与else if是两种格式,后者表示else作用域内额外嵌套一层if结构,因此比前者额外多一个end。

if(favor < 20)then
	return "{nick}的好感度还不够哟"
elseif(favor < 60)then
	return "只给{nick}一下哦~就一下"
else
	return "那就随{nick}的便啦"
end
for循环(数值)
for i=1, cnt_dice do	--表示i初始值为1,每次循环+1,大于cnt_dice时停止循环,相当于执行cnt_dice次
    local dice_point = ranint(1,6)
    table.insert(dice_pool,dice_point)  
    sum = sum + dice_point
end  
while()do 循环
while(cnt_dice > 0) do	--条件为真则继续循环,相当于执行cnt_dice次
    local dice_point = ranint(1,6)
    table.insert(dice_pool,dice_point)  
    sum = sum + dice_point
    cnt_dice = cnt_dice - 1
end  
repeat...until 循环
repeat	--第一次无条件执行,相当于执行cnt_dice次但至少为1次
    local dice_point = ranint(1,6)
    table.insert(dice_pool,dice_point)  
    sum = sum + dice_point
    cnt_dice = cnt_dice - 1
until( cnt_dice < 1 )	--条件为真则跳出循环

好了,现在你已经了解了lua的全部基本语法,可以自己动手自定义几乎任何跑团规则的掷骰指令,或是自制牌堆/好感互动指令了~

编写帮助

  • lua没有三目运算符,但可以通过短路运算符orand实现类似效果;

    0.1+0.2==0.3 and 'true' or 'false'

前缀指令脚本

Dice!在收到消息时,将先匹配基础指令(.authorize/.dismiss/.warning/.master/.bot/.helpdoc/.help),然后匹配自定义指令(Type=Order类reply),之后再执行内建指令,再匹配自定义回复(Type=Reply)。*(因此自定义指令可用于替换原有指令)*根据匹配到的前缀,Dice!将取到加载时注册的脚本文件和指令函数名,执行对应函数,并将消息未匹配的部分存为msg.suffix

指令函数

指令函数的参数msg可看做一张记录消息语境的表(实际为Lua类型userdata,以Dice定义的Context为元表),msg.fromMsg、msg.uid、msg.gid分别表示消息文本、来源群、来源用户。

指令函数的返回值可以有0到2个,如有,第一个返回值表示直接回复的语句,第二个返回值表示私聊回复的语句(用于暗骰/暗抽/暗检定,会同时发送给ob用户)。由于Dice!在发送消息时会将换页符'\f'视为消息分段发送,实际上可以通过一个返回值返回多段回复。

指令注册

lua脚本读取目录为[存档目录]/plugin/(plugin文件夹不会自动创建)

在启动或.system load时,Dice!会遍历plugin目录下的所有lua文件,读取msg_order表中的键值对。当消息文本前缀匹配到触发词时,Dice!将读取记录的文件名并调用指定函数。因此,可在运行期间实时修改脚本文件,只有增删文件、修改触发词或函数名需要.system load,只修改其他内容不需要重新加载,注意修改后保存即可。

脚本调试

作为一种脚本语言,lua的代码错误要到运行报错时才暴露。但lua虚拟机的报错并不会终结掉宿主C++程序,且Dice!会将Lua打印的报错信息发送到通知窗口。脚本可能在两个时点报错:加载脚本文件时和运行指令函数时。由于指令注册时会将整个lua文件作为脚本运行,因此基本的语法错误会导致此时立即报错,如多了个end,少了个括号,return之后没有end。通过在Lua在线运行工具上试跑脚本,可以提前发现第一类错误。但由于此时并不会真正执行函数体,所以更多更复杂的错误要在函数执行时报错(几乎全部是函数参数类型不正确),出现后需要第一时间热修复。

扩展说明

如果看不懂这部分内容,可以略过

指令注册时会将整个lua文件运行一遍。如果担心函数体过于复杂影响读取,建议将主体放置到plugin的子文件夹,使用loadLua调用。指令注册时不会递归遍历子文件夹,因此可以降低不必要的开销。例如,在Lua实现了Maid角色卡模板功能,保存在PC文件夹下Maid.lua,这样其他脚本可使用loadLua("PC/Maid")重复调用。

样例模板

--声明表msg_order,初始化时会遍历plugin文件,读取msg_order表中的指令名
--msg_order中的键值对表示 前缀指令->函数名
msg_order = {}
--声明触发词,允许多个触发词对应一个函数
order_word = "触发词"
order_word_1 = "触发词1"
order_word_2 = "触发词2"
--声明指令函数主体,函数名可自定义
function custom_order(msg)
    return "回复语句"
end
msg_order[order_word] = "custom_order"	--value为字符串格式且与指令函数名一致
msg_order[order_word_1] = "custom_order"
msg_order[order_word_2] = "custom_order"
--注意:本手册所提供脚本样例并非固定,仅基于模板化考虑选定该格式。

开发帮助

类型处理

lua使用type()string类型返回变量类型,js使用typeof()以字符串形式返回变量类型,python使用type()type类型返回变量类型。

文件路径 说明读写时机
[DiceData]/conf/BlackList.json黑名单记录修改时写入
[DiceData]/conf/Console.xmlMaster配置修改时写入
[DiceData]/conf/CustomCensor.json自定义敏感词库修改时写入
[DiceData]/conf/censor/敏感词库load时只读
[DiceData]/conf/CustomHelp.json自定义帮助文本修改时写入
[DiceData]/conf/CustomMsg.json自定义回执文本修改时写入
[DiceData]/conf/CustomMsgReply.json关键词回复文本修改时写入
[DiceData]/conf/LinkList.json链接索引修改时写入
[DiceData]/conf/ModList.json模块索引修改时写入
[DiceData]/conf/NoticeList.json通知窗口列表修改时写入
[DiceData]/conf/WebUIPassword.digestWebUI密码修改时写入
[DiceData]/user/ChatConf.dat群聊配置退出或save时写入
[DiceData]/user/DiceToday.json今日统计修改时写入
[DiceData]/user/HelpStatic.json帮助统计修改时写入
[DiceData]/user/PlayerCards.RDconf角色卡记录退出或save时写入
[DiceData]/user/UserConf.dat用户配置退出或save时写入
[DiceData]/user/session/团内记录(先攻、ob)修改时写入
[DiceData]/audit/消息通知日志更新时写入
[DiceData]/mod/模块文件load时只读
[DiceData]/plugin/脚本文件load及调用时只读
[DiceData]/PublicDeck/外置牌堆load时只读
[DiceData]/CardTemp/角色卡模板load时只读
变量type(lua)typeof(js)type(py)
未定义nilundefined报错:is not defined
1==0booleanbooleanbool
0numbernumberint
3.14numbernumberfloat
'hello'stringstringstr

常用语句

类型转换

lua字符串连接时会自动类型转换为字符串,但整数转换时会保留一位小数,需要手动使用string.format

--lua
local i = 233
local pi = 3.14
string.format("%d",i)	--整数转字符串,如果参数#2不为整数会报错
string.format("%.0f",pi)	--指定小数位数转字符串
print("pi="..math.pi)

JavaScript字符串连接时会自动类型转换

//js
console.log("pi="+)

python字符串连接时不会自动类型转换

#python
pi = 3.14
print("pi=" + str(pi))
字符串操作
--lua
--去除首尾空格
str:match("^[%s]*(.-)[%s]*$")
//js
//去除首尾空格
str.trim();
#python
#去除首尾空格
str.strip()
时间日期操作
--lua
--获取现在离1970年1月1日0时所过秒数
local t_now = os.time()

--格式化输出日期(YYYY-MM-DD HH:mm:ss,以系统所在时区)
local fmt_now = os.date("%Y-%m-%d %H:%M:%S")
local fmt_dt = os.date("%Y-%m-%d %H:%M:%S", t_now)
//js
var d_now = new Date();
//获取现在离1970年1月1日0时所过毫秒数
var t_now = new Date().getTime();

//格式化输出日期(YYYY-MM-DD HH:mm:ss,以系统所在时区)
function pad(num) {
    return num.toString().padStart(2, '0');
}
function formatDateTime(date = new Date()) {
    return date.getFullYear()+'-'+pad(date.getMonth() + 1)+'-'+pad(date.getDate())+' '
    +pad(date.getHours())+':'+pad(date.getMinutes())+':'+pad(date.getSeconds());
}
var fmt_now = formatDateTime()
var fmt_dt = formatDateTime(dt_now)
#python
import time
#获取现在离1970年1月1日0时所过秒数
t_now = time.time()

#格式化输出日期(YYYY-MM-DD HH:mm:ss,以系统所在时区)
fmt_now = time.strftime('%Y-%m-%d %H:%M:%S')
fmt_dt = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t_now))

文件的字符编码问题

除lua/js外的文件一律请以utf8编码。

Windows系统一般使用GBK字符集。Dice!支持utf-8及GBK两种字符集的lua文件,在读写字符串时将自动检测utf-8编码并转换。而出现以下情况时,编码并非二者皆可:

  • lua文件相互调用或读写其他文本文件,且字符串含有非ASCII字符时,关联文件字符集应保持一致
  • lua文件中需要调用http函数时,应当与目标网页的编码一致(基本是UTF8)
  • lua文件使用require或os等以文件名为参数的函数,且路径含有非ASCII字符时,必须使用GBK

附录:Dice!预置Lua元表及全局函数

**调用前注意Dice版本是否匹配!**Dice!2.7.0总计预置19条全局函数,4条http函数,4条context方法。

类型为number的参数,一般也可传入可数字化的字符串,如'msg.fromGroup'。

Context元表

语境Context,用于交互event或reply中事件上下文信息,如uid、gid等。

get(self, field, defaultVal)

取语境中变量,如#3省略,则context:get(field)可等效于context[field]

输入参数变量类型说明
语境Context形如context:get()可省略#1
变量字段string字段会先进行一次转义
候补值任意如未定义该变量则返回该值
返回值类型说明
任意待取变量
format(self, rawString)

在该语境下转义文本。

输入参数变量类型说明
语境Context形如msg:format()可省略#1
待转义文本string
返回值类型说明
string转义后文本
#### echo(self, replyMsg, isRaw)

回复消息,有来源聊天窗口的事件也可以用event:echo(replyMsg)

输入参数变量类型说明
语境Context形如msg:echo()可省略#1
待回复消息string
是否禁用转义boolean可省略,默认转义
inc(self, field, val)

自增变量,仅对数字有效。

输入参数变量类型说明
语境Context形如context:set()可省略#1
变量字段string字段会先进行一次转义
自增量number可省略,表示+1
### Actor元表

(build644+) 角色卡Actor

rollDice(exp)

调用角色卡的默认骰*(__DefaultDice)及默认掷骰表达式(__DefaultDiceExp)*进行掷骰,返回table记录掷骰结果。

输入参数变量类型说明
掷骰表达式string
返回值字段字段类型说明
exprstring规范化后的表达式
sumnumber掷骰结果(表达式合法时)
expansionstring掷骰展开式(表达式合法时)
errornumber错误类型(表达式非法时)

log(info[,notice_level])

*(build598+)*发送日志

输入参数变量类型说明
日志内容string待输出日志内容
通知窗口级别number选填,若空则只输出到框架日志界面

loadLua(scriptName)

运行Lua文件,返回目标脚本的返回值 参数使用相对路径且无后缀,根目录为plugin文件夹*(build575+)或mod内script文件夹(build575+)* 与Lua自带的require函数不同,目标文件定义的变量会保持生命周期

loadLua("PC/COC7")
输入参数变量类型说明
lua文件名string待调用mod/script/文件或plugin/文件
返回值类型说明
同文件内返回值类型执行指定文件后的返回值

ranint(low,high)

取随机数

输入参数变量类型说明
随机区间下限number整数
随机区间上限number整数
返回值类型说明
number生成随机数
### getDiceQQ()

取DiceMaid自身账号

返回值类型说明
string取骰子自身账号

getDiceDir()

取Dice存档目录,用于自行读写文件

返回值类型说明
string取Dice存档目录

eventMsg(msg, gid, uid)

虚构一条消息进行处理,不计入指令频度。可使用参数列表eventMsg(msg, gid, uid)或*(build608+)*参数包形式eventMsg(pkg).

eventMsg(".rc Rider Kick:70 踢西鹿", msg.gid, msg.uid)
eventMsg({
        fromMsg = ".rc Rider Kick:70 踢西鹿",
        gid = msg.gid,
        uid = msg.uid,
})
输入参数/pkg子项变量类型说明
消息文本/fromMsgstring
来源群/gidnumber可以为空
发送者/uidnumber

sendMsg(msg, gid, uid)

可使用参数列表sendMsg(msg, gid, uid)或*(build619+)*参数包形式sendMsg(pkg)发送.

sendMsg("早安哟", msg.fromGroup, msg.fromQQ)
输入参数/pkg子项变量类型说明
fwdMsgstring待发送消息
gidnumber私聊时为空
uidnumber群聊时可以为空
chidnumber频道id,仅参数包可用

getUserToday(userID, keyConf, defaultVal)

取用户今日数据项。特别地,配置项为"jrrp"时,所取值同.jrrp结果。所有当日数据会在系统时间24时清空。

getUserToday(msg.uid, "jrrp")
输入参数变量类型说明
用户账号number
配置项string待取配置项
候补值任意配置项不存在时返回该值,为空则返回0
返回值类型说明
任意待取值

setUserToday(userID, keyConf, val)

存用户今日数据项

输入参数变量类型说明
用户账号number
配置项string待存配置项
配置值任意待存入数据

getUserConf(userID, keyConf, defaultVal)

取用户配置,配置项带*标记表示会另行计算而非调用存储数据。*(build613+)*参数1可以为空,此时遍历所有记录了该属性的用户并返回以账号=属性值为键值对的table。

getUserConf(msg.fromQQ, "favor", 0)
getUserConf(nil, "favor") --返回所有用户的favor列表
输入参数变量类型说明
用户账号number
配置项string待取配置项
候补值任意配置项不存在时返回该值
返回值类型说明
任意待取值
特殊配置项说明
trust用户信任(仅4以下可编辑)
firstCreate用户记录创建(初次使用)时间 [时间戳,秒]
lastUpdate用户记录最后更新时间 [时间戳,秒]
name*用户账号昵称(只读)
nick*全局称呼(备取账号昵称)
nick#`群号`*特定群内的称呼(备取群名片->全局称呼->账号昵称)
nn*全局nn
nn#`群号`*特定群内的nn

setUserConf(userID, keyConf, val)

存用户配置项

输入参数变量类型说明
用户账号number
配置项string待存配置项
配置值任意待存入数据

getGroupConf(groupID, keyConf, defaultVal)

取群配置,配置项带*标记表示会另行计算而非调用存储数据。*(build613+)*群号可以为空,此时遍历所有记录了该属性的群并返回以群号=属性值为键值对的table。

getGroupConf(msg.fromQQ, "rc房规", 0)
输入参数变量类型说明
群号number
配置项string待取配置项
候补值任意配置项不存在时返回该值
返回值类型说明
任意待取值
特殊配置项说明
name*群名称(只读)
size*群人数(只读)
maxsize*群规模(只读)
firstCreate用户记录创建(初次使用)时间 [时间戳,秒]
lastUpdate用户记录最后更新时间 [时间戳,秒]
members群用户列表
admins群管理列表
card#`群员账号`*群名片
auth#`群员账号`*群权限(只读) 1-群员;2-管理;3-群主
lst#`群员账号`*最后发言时间(只读) [时间戳,秒]

setGroupConf(groupID, keyConf, val)

存群配置项

输入参数变量类型说明
群号number
配置项string待存配置项
配置值任意待存入数据

getPlayerCard(userID, groupID)

取指定群内绑定的角色卡(整张)

输入参数变量类型说明
用户账号number
群号string为空或未绑定则取默认卡

getPlayerCardAttr(userID, groupID, keyAttr, defaultVal)

取角色卡属性

getPlayerCardAttr(msg.fromQQ, msg.fromGroup, "理智", val_default)
输入参数变量类型说明
用户账号number
群号number
属性名string待取属性
候补值任意属性不存在时返回该值

setPlayerCardAttr(userID, groupID, keyConf, val)

存角色卡属性

返回值类型说明
任意待取属性
输入参数变量类型说明
用户账号number
群号number
属性名string待存属性
属性值任意待存数据

mkDirs(pathDir)

输入参数变量类型说明
文件夹路径string递归创建该文件夹

sleepTime(ms)

输入参数变量类型说明
等待毫秒数number
### http

(build590+)

http.get
输入参数变量类型说明
待访问urlstring若含须转义字符须用urlEncode转义
返回值变量类型说明
连接是否成功boolean
网页返回内容string访问失败则返回错误原因
#### http.post
输入参数变量类型说明
待访问urlstring若含须转义字符须用urlEncode转义
post数据string(build634+)如为table则自动序列化为json格式
headerstring(build606+)可省略,默认Content-Type: application/json,(build629+)如为table将自动拼接
返回值变量类型说明
连接是否成功boolean
网页返回内容string访问失败则返回错误原因
#### http.urlEncode

将url中须转义的字符进行转义。

输入参数变量类型说明
待编码urlstring
返回值变量类型说明
编码后urlstring
http.urlDecode

还原url中转义的字符。

输入参数变量类型说明
待解码urlstring
返回值变量类型说明
解码后urlstring
## 附录:Dice预置JavaScript原型及全局函数

(build640+)

Actor原型

(build644+) 角色卡Actor

rollDice(exp)

调用角色卡的默认骰*(__DefaultDice)及默认掷骰表达式(__DefaultDiceExp)*进行掷骰,返回table记录掷骰结果。

输入参数变量类型说明
掷骰表达式string
返回值字段字段类型说明
exprString规范化后的表达式
sumNumber掷骰结果(表达式合法时)
expansionString掷骰展开式(表达式合法时)
errorNumber错误类型(表达式非法时)

log(info[,notice_level])

发送日志

输入参数变量类型说明
日志内容String待输出日志内容
通知窗口级别Number选填,若空则只输出到框架日志界面

getDiceID()

取DiceMaid自身账号

返回值类型说明
Number取骰子自身账号
### getDiceDir()

取Dice存档目录,用于自行读写文件

返回值类型说明
String取Dice存档目录
### eventMsg(msg, gid, uid)

虚构一条消息进行处理,不计入指令频度。可使用参数列表eventMsg(msg, gid, uid)或*(build608+)*参数包形式eventMsg(pkg).

eventMsg(".rc Rider Kick:70 踢", msg.gid, msg.uid)
eventMsg({
        fromMsg = ".rc Rider Kick:70 踢",
        gid = msg.gid,
        uid = msg.uid,
})
输入参数/pkg子项变量类型说明
消息文本/fromMsgString
来源群/gidNumber可以为空
发送者/uidNumber

sendMsg(msg, gid, uid)

可使用参数列表sendMsg(msg, gid, uid)或*(build619+)*参数包形式sendMsg(pkg)发送.

sendMsg("早安哟", msg.fromGroup, msg.fromQQ)
输入参数/pkg子项变量类型说明
fwdMsgString待发送消息
gidNumber私聊时为空
uidNumber群聊时可以为空
chidNumber频道id,仅参数包可用

getUserToday(userID, keyConf, defaultVal)

取用户今日数据项。特别地,配置项为"jrrp"时,所取值同.jrrp结果。所有当日数据会在系统时间24时清空。

getUserToday(msg.uid, "jrrp")
输入参数变量类型说明
用户账号Number
配置项String待取配置项
候补值任意配置项不存在时返回该值,为空则返回0
返回值类型说明
任意待取值

setUserToday(userID, keyConf, val)

存用户今日数据项

输入参数变量类型说明
用户账号Number
配置项String待存配置项
配置值任意待存入数据

getUserAttr(userID, keyConf, defaultVal)

取用户配置,配置项带*标记表示会另行计算而非调用存储数据。*(build613+)*参数1可以为空,此时遍历所有记录了该属性的用户并返回以账号=属性值为键值对的table。

getUserConf(msg.fromQQ, "favor", 0)
getUserConf(nil, "favor") --返回所有用户的favor列表
输入参数变量类型说明
用户账号Number
配置项String待取配置项
候补值任意配置项不存在时返回该值
返回值类型说明
任意待取值
特殊配置项说明
trust用户信任(仅4以下可编辑)
firstCreate用户记录创建(初次使用)时间 [时间戳,秒]
lastUpdate用户记录最后更新时间 [时间戳,秒]
name*用户账号昵称(只读)
nick*全局称呼(备取账号昵称)
nick#`群号`*特定群内的称呼(备取群名片->全局称呼->账号昵称)
nn*全局nn
nn#`群号`*特定群内的nn

setUserAttr(userID, keyConf, val)

存用户配置项

输入参数变量类型说明
用户账号Number
配置项String待存配置项
配置值任意待存入数据

getGroupAttr(groupID, keyConf, defaultVal)

取群配置,配置项带*标记表示会另行计算而非调用存储数据。*(build613+)*群号可以为空,此时遍历所有记录了该属性的群并返回以群号=属性值为键值对的table。

getGroupConf(msg.fromQQ, "rc房规", 0)
输入参数变量类型说明
群号Number
配置项String待取配置项
候补值任意配置项不存在时返回该值
返回值类型说明
任意待取值
特殊配置项说明
name*群名称(只读)
size*群人数(只读)
maxsize*群规模(只读)
firstCreate用户记录创建(初次使用)时间 [时间戳,秒]
lastUpdate用户记录最后更新时间 [时间戳,秒]
members群用户列表
admins群管理列表
card#`群员账号`*群名片
auth#`群员账号`*群权限(只读) 1-群员;2-管理;3-群主
lst#`群员账号`*最后发言时间(只读) [时间戳,秒]

setGroupAttr(groupID, keyConf, val)

存群配置项

输入参数变量类型说明
群号Number
配置项String待存配置项
配置值任意待存入数据

附录:Dice预置Python类及模块函数

*(build639+)*Dice!2.7.0总计预置dicemaid模块19条函数,4条context方法。dicemaid模块已在初始化时import,故可以如全局函数般调用。

Actor类

(build644+) 角色卡Actor

rollDice(exp)

调用角色卡的默认骰*(__DefaultDice)及默认掷骰表达式(__DefaultDiceExp)*进行掷骰,返回table记录掷骰结果。

输入参数变量类型说明
掷骰表达式string
返回值字段字段类型说明
exprString规范化后的表达式
sumNumber掷骰结果(表达式合法时)
expansionString掷骰展开式(表达式合法时)
errorNumber错误类型(表达式非法时)

log(info[,notice_level])

发送日志

输入参数变量类型说明
日志内容String待输出日志内容
通知窗口级别Number选填,若空则只输出到框架日志界面

getDiceID()

取DiceMaid自身账号

返回值类型说明
Number取骰子自身账号
### getDiceDir()

取Dice存档目录,用于自行读写文件

返回值类型说明
String取Dice存档目录

eventMsg(msg, gid, uid)

虚构一条消息进行处理,不计入指令频度。可使用参数列表eventMsg(msg, gid, uid)或*(build608+)*参数包形式eventMsg(pkg).

eventMsg(".rc Rider Kick:70 踢", msg.gid, msg.uid)
eventMsg({
        fromMsg = ".rc Rider Kick:70 踢",
        gid = msg.gid,
        uid = msg.uid,
})
输入参数/pkg子项变量类型说明
消息文本/fromMsgString
来源群/gidNumber可以为空
发送者/uidNumber

sendMsg(msg, gid, uid)

可使用参数列表sendMsg(msg, gid, uid)或*(build619+)*参数包形式sendMsg(pkg)发送.

sendMsg("早安哟", msg.fromGroup, msg.fromQQ)
输入参数/pkg子项变量类型说明
fwdMsgString待发送消息
gidNumber私聊时为空
uidNumber群聊时可以为空
chidNumber频道id,仅参数包可用

getUserToday(userID, keyConf, defaultVal)

取用户今日数据项。特别地,配置项为"jrrp"时,所取值同.jrrp结果。所有当日数据会在系统时间24时清空。

getUserToday(msg.uid, "jrrp")
输入参数变量类型说明
用户账号Number
配置项String待取配置项
候补值任意配置项不存在时返回该值,为空则返回0
返回值类型说明
任意待取值

setUserToday(userID, keyConf, val)

存用户今日数据项

输入参数变量类型说明
用户账号Number
配置项String待存配置项
配置值任意待存入数据

getUserAttr(userID, keyConf, defaultVal)

取用户配置,配置项带*标记表示会另行计算而非调用存储数据。*(build613+)*参数1可以为空,此时遍历所有记录了该属性的用户并返回以账号=属性值为键值对的table。

getUserConf(msg.fromQQ, "favor", 0)
getUserConf(nil, "favor") --返回所有用户的favor列表
输入参数变量类型说明
用户账号Number
配置项String待取配置项
候补值任意配置项不存在时返回该值
返回值类型说明
任意待取值
特殊配置项说明
trust用户信任(仅4以下可编辑)
firstCreate用户记录创建(初次使用)时间 [时间戳,秒]
lastUpdate用户记录最后更新时间 [时间戳,秒]
name*用户账号昵称(只读)
nick*全局称呼(备取账号昵称)
nick#`群号`*特定群内的称呼(备取群名片->全局称呼->账号昵称)
nn*全局nn
nn#`群号`*特定群内的nn

setUserAttr(userID, keyConf, val)

存用户配置项

输入参数变量类型说明
用户账号Number
配置项String待存配置项
配置值任意待存入数据

getGroupAttr(groupID, keyConf, defaultVal)

取群配置,配置项带*标记表示会另行计算而非调用存储数据。*(build613+)*群号可以为空,此时遍历所有记录了该属性的群并返回以群号=属性值为键值对的table。

getGroupConf(msg.fromQQ, "rc房规", 0)
输入参数变量类型说明
群号Number
配置项String待取配置项
候补值任意配置项不存在时返回该值
返回值类型说明
任意待取值
特殊配置项说明
name*群名称(只读)
size*群人数(只读)
maxsize*群规模(只读)
firstCreate用户记录创建(初次使用)时间 [时间戳,秒]
lastUpdate用户记录最后更新时间 [时间戳,秒]
members群用户列表
admins群管理列表
card#`群员账号`*群名片
auth#`群员账号`*群权限(只读) 1-群员;2-管理;3-群主
lst#`群员账号`*最后发言时间(只读) [时间戳,秒]
### setGroupAttr(groupID, keyConf, val)

存群配置项

输入参数变量类型说明
群号Number
配置项String待存配置项
配置值任意待存入数据

附录:自定义指令常用的Lua正则匹配

解析参数时,可使用msg.suffix来略过指令前缀匹配的部分,从之后的字符开始匹配。

--前缀匹配且指令参数为消息余下部分时,去除前后两端的空格
local rest = msg.suffix:match("^[%s]*(.-)[%s]*$")

--指令参数为单项整数时,直接用%d+表示匹配一个或多个数字,未输入数字时匹配失败返回nil
local cnt = string.match(msg.suffix,"%d+")

--同上,%d*表示匹配0或任意个数字,未输入数字时匹配成功返回空字符串""
--该匹配模式需要确保数字之前的其他字符已被排除
local cnt = string.match(msg.suffix,"%d*")

--参数使用空格分隔且不限数目,遍历匹配
local item,rest = "",string.match(msg.fromMsg,"^[%s]*(.-)[%s]*$",#order_select_name+1)
if(rest == "")then
    return "请输入参数"
end
local items = {}
repeat
    item,rest = string.match(rest,"^([^%s]*)[%s]*(.-)$")
    table.insert(items, item)
until(rest=="")

附录:常用在线语法校验/运行工具

附录:lua常见报错说明

module '%s' not found:
	no field package.preload['%s']
	no file 
-- 没有把被引用的lua或dll文件放在指定位置(多见于require与loadLua)
-- 解决方式:把所需文件放入Dice存档目录/plugin/或Diceki/lua/,dll文件或require对象必须置于后者
attempt to call a nil value (global '%s')
-- 将空变量%s用作函数(global表示被调用的是全局变量,local表示本地变量,method表示索引方法)
attempt to index a nil value (global '%s')
-- 对空变量%s使用索引(只有table等结构可以索引,形如msg.fromMsg)
attempt to concatenate a nil value (local '%s')
-- 使用..连接字符串时连接了一个空变量%s
bad argument #1 to '%s' (string expected, got nil)
-- 函数%s的第1个参数类型错误,要求类型为string,但实际传入的参数为nil。特别地,got nil表示输入参数为nil或缺少参数
value expected
-- 要求参数,但没有传入
attempt to perform arithmetic on a nil value (global '%s')
-- 将一个空变量%s当做数字表示
bad argument #5 to 'format'(number has no integer representation)
-- 函数format的第5个参数类型错误,要求是整数,但传入是小数,或者是其余类型不能化为整数
'}' expected (to close '{' at line 169) near '%s'
-- 脚本第169行的左花括号缺少配对的右花括号。此错误也可以由表格内缺少逗号分隔、括号外的中文等原因造成
'end' expected (to close 'function' at line 240) near <eof>
-- 脚本第240行的function缺少收尾的end,<eof>表示文件结束(找到文件末也没找到)
'then' expected near 'end'
-- if then end逻辑结构缺少then
unexpected symbol near '%s'
-- 符号%s边有无法识读的符号,比如中文字符
attempt to get length of a nil value (local 'tab')
-- 对空变量tab作取长度运算(#)
attempt to add a 'string' with a 'string'
-- 对(不能化为数字的)字符串用加法'+'(字符串只能用连接'..')
attempt to compare number with string
-- 对数字和(不能化为数字的)字符串用比较运算符
error loading module '%s' from file
-- 使用require "%s"时加载出错
no visible label '%s' for <goto> at line 
-- 在循环结构中跳转不存在的节点
invalid option '%s'
-- 传入的参数不是string或不在给定的字符串列表中
malformed number near '86400..'
-- 数字与字符连接符粘连导致误判为小数点,使得格式解析错误,需要插入空格

附录:事件event样例

代理事件

时点说明
StartUp启动完成后
FriendRequest好友申请后,处理前
FriendAdd自动或被同意添加好友后,回复前
GroupRequest受群邀请后,处理前
GroupKicked被踢出群后,拉黑前
GroupBanned被禁言后,拉黑前
GroupAuthorize群申请许可后,处理前
LogEnd日志完成后,上传前
WhisperIgnored私聊不识别为指令且未触发回复
DayEnd(时差修正后)0点后刷新每日数据前
DayNew刷新每日数据后
- *被踢出群不含解散,被禁言不含全群禁言* - *GroupAuthorize、LogEnd、WhisperIgnored时点在消息处理时,对应Event为Message,其余事件Event为其自身*
event参数说明
uid当事用户
gid当事群
fromMsg消息文本/好友申请文本
aimGroupGroupAuthorize中待审核群
AttachInfoGroupAuthorize中申请文本
log_file/log_pathLogEnd中日志文件名及路径
blocked[回传]是否拦截该事件后续处理
approval[回传]是否同意申请
ignored[回传]是否忽略算入指令计数
--event/hook.lua
event.listen_startup = {
    title = "启动完成",
    trigger = {
        hook = "StartUp"
    },
    action = { lua = "listen_startup" } 
}
event.listen_friend_request = {
    title = "好友申请处理",
    trigger = {
        hook = "FriendRequest"
    },
    action = { lua = "listen_friend_request" } 
}
event.listen_friend_add = {
    title = "好友添加事件",
    trigger = {
        hook = "FriendAdd" --主动通过好友申请时不触发
    },
    action = { lua = "listen_friend_add" } 
}
代理好友申请
--script/listen_friend_requst.lua
answer = event.fromMsg
--如果验证方式为回答问题,则需要截去问题部分
-answer = string.sub(answer,string.find(answer,"答案:")+#"答案:")
true_answer = "正确回案"
if string.find(answer,"true_answer") then
    log("收到"..getUserConf(event.uid,"name").."("..event.fromUser.."的好友请求:\n"..answer.."\n回答通过√",1)
    event.approval = true --通过申请
else
    log("收到"..getUserConf(event.uid,"name").."("..event.fromUser.."的好友请求:\n"..answer.."\n回答错误×",1)
    event.approval = false
end
event.blocked = true --终止连锁,不执行原生申请处理流程
代理新增好友
--script/listen_friend_add.lua
answer = event.fromMsg
--如果验证方式为回答问题,则需要截去问题部分
-answer = string.sub(answer,string.find(answer,"答案:")+#"答案:")
true_answer = "正确回案"
if string.find(answer,"true_answer") then
    log("收到"..getUserConf(event.fromUser,"name").."("..event.fromUser.."的好友请求:\n"..answer.."\n回答通过√",1)
    event.approval = true --通过申请
else
    log("收到"..getUserConf(event.fromUser,"name").."("..event.fromUser.."的好友请求:\n"..answer.."\n回答错误×",1)
    event.approval = false
end
event.blocked = true --终止连锁,不执行原生申请处理流程
代理公骰审核
--script/listen_group_authorize.lua
answer = event.attachText
--如果验证方式为回答问题,则需要截去问题部分
-answer = string.sub(answer,string.find(answer,"答案:")+#"答案:")
true_answer = "正确回案"
if string.find(answer,"true_answer") then
    log("收到"..getUserConf(event.fromUser,"name").."("..event.fromUser.."的好友请求:\n"..answer.."\n回答通过√",1)
    event.approval = true --通过申请
else
    log("收到"..getUserConf(event.fromUser,"name").."("..event.fromUser.."的好友请求:\n"..answer.."\n回答错误×",1)
    event.approval = false
end
event.blocked = true --终止连锁,不执行原生申请处理流程

附录:DND规则.rdc指令

.rdc(B/P) (+/-[加值]) ([检定理由]) ([成功阈值]) 参数[优/劣势骰]:可选,B=2D20取大,P=2D20取小 参数[加值]:可选,加值最后修正到D20结果上 参数[检定理由]:可选,将显示在回执语句中 参数[成功阈值]:可选,将与掷骰结果比较,返回成功或失败 出于及时向用户提供指令帮助的考虑,空参将返回帮助

msg_order = {}

function intostring(num)
    if(num==nil)then
        return ""
    end
    return string.format("%.0f",num)
end

--DND
function roll_d20(msg)
    local rest=string.match(msg.fromMsg,"^[%s]*(.-)[%s]*$", 5)
    if(rest=="")then
        return [[
D20检定:.rdc
.rdc(B/P) (+/-[加值]) ([检定理由]) ([成功阈值])
参数[优/劣势骰]:可选,B=2D20取大,P=2D20取小
参数[加值]:可选,加值最后修正到D20结果上
参数[检定理由]:可选,将显示在回执语句中
参数[成功阈值]:可选,将与掷骰结果比较,返回成功或失败
空参返回本段说明文字]]
    end
    local bonus,sign,rest = string.match(rest,"^[%s]*([bBpP]?)[%s]*([+-]?)(.*)$")
    local addvalue = 0
    local modify = ""
    --加减符号判断有无加值
    if(sign ~= "")then
        modify,rest = string.match(rest,"^([%d]+)[%s]*(.*)$")
        if(modify==nil or modify=="")
        then
            return "请{pc}输入正确的加值"
        end
        addvalue = tonumber(modify)
        if(sign=='-')
        then
            addvalue = addvalue*-1;
        end
    end
    local skill,dc = string.match(rest,"^[%s]*(.-)[%s]*([%d]*)$")
    local dc = tonumber(dc)
    if(dc~=nil)then
        if(dc<1)then
            return "这你要怎么才能失败啊?"
        elseif(dc>99)then
            return "这你要怎么才能成功啊?"
        end
    end
    local die = ranint(1,20)
    local selected = die
    local res
    --考虑优势骰/劣势骰
    if(bonus=="")then
        res="D20"..sign..modify.."="..intostring(die)..sign..modify
    else
        local another=ranint(1,20)
        bonus=string.upper(bonus)
        if((another>die and bonus=="B")or(another<die and bonus=="P"))then
            selected=another
        end
        res=bonus.."("..intostring(die)..','..intostring(another)..")"..sign..modify.."->"..intostring(selected)..sign..modify
    end
    local strReply = "{pc}进行"..skill.."检定:"..res
    local final=selected+addvalue
    if(addvalue~=0)then
        strReply=strReply.."="..intostring(final)
    end
    if(dc=="")then
        --对成功与否不做判定
    elseif(final>dc)
    then 
        strReply=strReply..'>'..intostring(dc).." {strRollRegularSuccess}"
    elseif(final<dc)
    then
        strReply=strReply..'<'..intostring(dc).." {strRollFailure}"
    else
        strReply=strReply..'='..intostring(dc).." {strRollRegularSuccess}"
    end
    return strReply
end
msg_order[".rdc"] = "roll_d20"

附录:ShadowRun规则.rsr指令

ShadowRun掷骰规则:掷X粒六面骰,点数为5或6记一次命中,过半数骰目出1记失误。自定义指令格式.rsr X。

msg_order = {}

function intostring(num)
    if(num==nil)then
        return ""
    end
    return string.format("%.0f",num)
end

--shadowrun
function roll_shadowrun(msg)
    local cnt = tonumber(string.match(msg.fromMsg,"%d+",5))
    if(not cnt)
    then
        return "shadowrun检定:.rsr[骰数] 出5/6视为命中\n出1过半视为失误"
    end
    local res="{pc}掷骰"..cnt.."D6: "
    cnt = tonumber(cnt)
    if(cnt<1)
    then
        return "{strZeroDiceErr}"
    elseif(cnt>100)
    then
        return "{strDiceTooBigErr}"
    else
        local cntSuc=0
        local cntFail=0
        local pool={}
        local glitch=""
        for times=1,cnt do
            local die=ranint(1,6)
            if(die>=5)then
                cntSuc = cntSuc + 1
            elseif(die==1)then
                cntFail = cntFail + 1
            end
            table.insert(pool,intostring(die))
        end
        if(cntFail > cnt/2)then
            glitch = " {strGlitch}"
        end
        local reply=res..table.concat(pool,'+').."->"..intostring(cntSuc)..glitch
        return reply
    end
end
msg_order[".rsr"] = "roll_shadowrun"

附录:仿MDice的.duel指令

msg_order = {} 

function intostring(num)
    if(num==nil)then
        return ""
    end
    return string.format("%.0f",num)
end

--惠惠给每个得分都写了专属回复,这里作为样例仅使用随机选取
reply_duel_win = {
    "不服?不服你就正面赢我啊",
    "你请我吃顿饭这次就算你赢,怎么样?",
    "哦吼?我觉得我甚至可以让你一个骰子",
    "(面露笑意地看着你)",
    "你觉得能赢我,嘿嘿,那是个幻觉,美丽的幻觉。",
    "再试也没用啦,会输一次就会输一百次哦",
}
reply_duel_lose = {
    "?!居然趁我一时大意。。。"
}
reply_duel_even = {
    "哼哼,平分秋色嘛"
}

function table_draw(tab)
    return tab[ranint(1,#tab)]
end

--惠系决斗
function mdice_duel(msg)
    local isDark=string.sub(msg.fromMsg,1,5)==".dark"
    local poolSelf, poolPlayer = {},{}
    local resSelf, resPlayer, resTotal, resEval = "","","",""
    local sumSelf, sumPlayer, sumTotal=0,0,0
    for times=1, 5 do
        local die = ranint(1,100)
        sumSelf = sumSelf + die
        table.insert(poolSelf,intostring(die))
    end
    resSelf="\n{self}\n"..table.concat(poolSelf,'、').."\n打点"..intostring(sumSelf)
    for times=1, 5 do
        local die = ranint(1,100)
        sumPlayer = sumPlayer + die
        table.insert(poolPlayer,intostring(die))
    end
    resPlayer="\n{pc}\n"..table.concat(poolPlayer,'、').."\n打点"..intostring(sumPlayer)
    sumTotal = sumPlayer-sumSelf;
    resTotal="\n————\n计"..intostring(sumTotal).."/10="..intostring(sumTotal/10).."分"
    if(sumSelf>sumPlayer)then
        if(isDark)then
            resEval = "发起黑暗决斗的你,应该做好觉悟了吧?"
            --好孩子不要用 eventMsg(".group ".. msg.fromGroup .." ban ".. msg.fromQQ .." "..intostring(sumSelf), 0, getDiceQQ())
        else
            resEval = table_draw(reply_duel_win)
        end
    elseif(sumSelf<sumPlayer)then
        resEval = table_draw(reply_duel_lose)
    else
        resEval = table_draw(reply_duel_even)
    end
    local reply = "看起来{pc}想向我发起决斗,呼呼,那我就接下了\n胜负的结果是——"..resSelf..resPlayer..resTotal.."\n"..resEval
    return reply --如有返回值表示回复文本,如有第二返回值表示暗骰文本
end
msg_order[".dark duel"] = "mdice_duel"
msg_order[".duel"] = "mdice_duel"
--指令注册格式 key->指令前缀 value->脚本内函数名

附录:仿SitaNya的.clock指令

msg_order = {} 

function intostring(num)
    if(num==nil)then
        return ""
    end
    return string.format("%.0f",num)
end

function print_chat(msg)
    if(msg.fromGroup == "0")then
        return "qq "..msg.fromQQ
    end
    return "group "..msg.fromGroup
end

function Sleep(n)
   --575+实现方式
   if n > 0 then sleepTime(n*1000) end
end

function sitanya_clock(msg)
    local seconds = string.match(msg.fromMsg,"%d+",7)
    if(not seconds)then
        return [[clock    计时器
出于技术验证仿塔骰写的指令,请勿滥用该指令。
用法:.clock [秒]
示例:.clock 60]]
    end
    if(tonumber(seconds)>600)then
        return "设置{self}定时失败:设置时长超过10分钟×"
    end
    eventMsg(".send "..print_chat(msg).." 您的定时器:<".. seconds ..">秒已开启计时",0,getDiceQQ())
    Sleep(seconds)
    return "{nick}的定时器:<".. seconds ..">秒已到期"
end
msg_order[".clock"] = "sitanya_clock"