koishi-plugin-giveaway
v1.0.1
Published
A versatile giveaway plugin for Koishi: interactive giveaway creation, scheduled auto-draw, reminders, i18n and multi-timezone support.
Maintainers
Readme
koishi-plugin-giveaway
一个多功能的 Koishi 群抽奖插件:复制模板即可创建抽奖、支持参与门槛(群聊等级 / 活跃度 / QQ 群互动标识)、口令或指令两种参与方式、定时自动开奖与开奖前提醒、可选图片卡片,权限沿用 Koishi 原生等级,内置简体中文 / English / Deutsch 三语与多时区。
A giveaway plugin for Koishi: create a giveaway by filling in a text template, join conditions (group level / activity / QQ group honors), keyword or command joining, scheduled auto-draw with reminders, optional image cards, Koishi-native permissions, i18n (zh-CN / en-US / de-DE) and multi-timezone support.
本项目派生自 Roll-Bot-Project/roll-bot(MIT),本仓库独立维护与发布,问题请提到本仓库。
目录
完整的功能清单与交互矩阵见 docs/features-and-interactions.md;发版步骤与冒烟清单见 docs/release-checklist.md。
维护状态
- 1.0.0 是首个正式版本,功能面已稳定:1.0 起配置项与数据结构只增不删,小版本只修缺陷
- 预期使用规模不大,因此 1.0 之后不做常规迭代:只有在社区反馈缺陷或有明确需求时才更新
- 不承诺与 0.x 的兼容:从 0.x 升级请按本 README 重新配置(配置项含义与数据模型都按当前版本为准);
0.x 的变更历史见
CHANGELOG-0.x.md - 遇到问题请提 Issue,附上
抽奖接口诊断的输出与插件日志
特性
创建与参与
- 复制模板创建:
创建抽奖发一份纯文本模板(含当前控制台的参与条件),逐项填写后发回即可;留空即默认,按标签解析,群里随口一句不会被误当成奖品 - 每个抽奖可单独设置参与条件:模板里的「参与条件」一行可沿用控制台配置、删空(该抽奖不限制)或改写(自定义门槛)
- 两种参与方式:在群里发送抽奖的加入口令,或使用
加入抽奖 <编号> - 拒绝时说明原因:等级不足 / 最近没发言 / 连续天数不够 / 缺少标识;参与者被拒之前也能在详情里看到当前生效的条件
抽奖流程
- 定时自动开奖:创建时填了开奖时间即到点自动开奖,也可随时手动
开奖;开奖后按cacheHours自动清理记录 - 开奖消息 @ 中奖者:中奖行是真正的 @ 元素(QQ 里会提醒到人),并保留 QQ 号便于留档
- 开奖前提醒:按「创建时距开奖的剩余时长」分档,在剩余时长的百分比处自动播报(默认每场只提醒一次)
- 可选的图片卡片:安装并启用
koishi-plugin-puppeteer后,创建结果 / 抽奖详情 / 参与名单 / 抽奖列表 / 开奖结果都可渲染成图片;未安装或渲染失败时自动回退为文字消息
参与条件(可组合,任一不满足即拒绝并说明原因)
- 群聊等级下限(QQ 群聊等级 1~100)
- 最近 N 天内发过言(不依赖 QQ 网页接口,最稳的条件)
- 最长连续发言天数(可自定义门槛,如「连续 ≥ 14 天」)
- QQ 群互动标识:群聊之火 / 群聊炽焰 / 龙王,可选「满足任一」或「必须全部」;龙王还可在「昨日活跃榜」与「仅当前龙王」之间切换口径
其它
- 权限沿用 Koishi 原生等级(0 封禁 / 1 用户 / 3 管理员 / 4 超管),创建者始终可管理自己的抽奖
- 三语 + 多时区:群消息、指令表、控制台配置说明、图片卡片标签都跟随语言;开奖时间按用户 / 频道 / 默认时区逐级解析
- 参与名单:详情与
抽奖成员都能列出参与者(头像 + 昵称 + QQ 号),图片里可限制最多显示几人 - 诊断指令:管理员可一键排查群成员 / 群荣誉取数接口,原始返回写入插件日志
安装
# Koishi 控制台:插件市场搜索 giveaway 安装
# 或
npm i koishi-plugin-giveaway- 必需服务:
database(抽奖记录)、assets(图片资源) - 可选服务:
puppeteer(图片卡片;不装则全部走文字消息)
指令列表
所有指令都挂在根指令 giveaway(别名 抽奖)下。子指令可写全路径(giveaway.detail)或直接用别名(抽奖详情);giveaway.h 这类短别名需连同 giveaway 一起输入。前缀由应用配置决定。
| 指令 | 中文别名 / 短别名 | 参数 / 选项 | 权限 |
| --- | --- | --- | --- |
| giveaway | 抽奖 | — | 任何人(输出指令一览) |
| giveaway.help | 抽奖帮助、giveaway.h | — | 任何人 |
| giveaway.add | 创建抽奖 | [奖品] [开奖时间] [口令];-t 标题 / -d 描述 / -r 允许重复中奖 | ≥ authorityCreate(默认 1)或群主/群管理员 |
| giveaway.list | 抽奖列表、在抽啥、giveaway.ls | [平台] [频道id] | 任何人 |
| giveaway.detail | 抽奖详情 | <编号> | 任何人 |
| giveaway.member | 抽奖成员、giveaway.mem | <编号> | 任何人 |
| giveaway.join | 加入抽奖、giveaway.j | <编号> | 任何人(需满足参与条件) |
| giveaway.quit | 退出抽奖、giveaway.q | <编号> | 任何人 |
| giveaway.end | 开奖、giveaway.draw | [编号] | ≥ authorityManage(默认 3)、创建者,或群主/群管理员(可关) |
| giveaway.delete | 删除抽奖、giveaway.rm | <编号> | ≥ authorityManage、创建者,或群主/群管理员(可关) |
| giveaway.time | 时区 | [偏移];-c 本频道 / -d 默认 | ≥ authorityManage 或群主/群管理员 |
| giveaway.locale | 语言 | [语言];-c 本频道 / -d 默认 | ≥ authorityManage 或群主/群管理员 |
| giveaway.channel | 频道id | — | 任何人(查看当前频道 id) |
| giveaway.debug.honor | 抽奖接口诊断 | — | ≥ authorityManage |
| giveaway.debug.member | 抽奖成员诊断 | [用户] | ≥ authorityManage |
直接发
抽奖(或抽奖帮助)会输出一份按语言分组的指令一览:中文环境显示中文别名,英文 / 德文环境显示注册名,管理员额外看到诊断指令。
交互详解
创建抽奖
> 创建抽奖
< 请复制下面这份模板,把每一项填好后发回给我(不需要的项留空即可):
< 奖品:
< 开奖时间:
< 加入口令:
< 标题:
< 描述:
< 参与条件:等级≥40 活跃≥1 标识=群聊炽焰,龙王
< 说明一:奖品格式为「名称*数量」,多个奖品用逗号或空格分隔;开奖时间格式为「月-日-时-分」或「年-月-日-时-分」,留空表示不自动开奖。
< 说明二:「参与条件」一行可以不改(沿用控制台配置)、删空(本抽奖不做限制),或照「等级≥40 活跃≥1 连续≥7 标识=群聊炽焰,龙王」改写。
> 奖品:显卡*1,鼠标*2
> 开奖时间:09-15-20-00
> 加入口令:参加
> 标题:双十一抽奖
> 描述:满 40 级可参加- 一次往返即可完成;解析按标签取值,顺序无关,中 / 英 / 德标签与全角冒号都能识别,整段复制粘贴(含说明行)也可用
- 留空的项按默认处理:开奖时间留空 = 不自动开奖、口令留空 = 不用口令、标题 / 描述留空 = 默认值;只有奖品必填
- 奖品格式
名称*数量(数量可省略,默认 1);多个奖品可用逗号 / 顿号 / 空格分隔 - 取消创建:回复
q/quit/cancel/取消/abbrechen、超时不回、缺奖品或格式非法都会取消 - 开奖时间必须晚于当前时间(填过去的时间会要求重新填写,避免出现永远不会自动开奖的抽奖)
- 位置参数写法(跳过模板步骤):
抽奖 add 显卡*1 09-15-20-00 参加,-t/-d/-r同样可用 - 参与条件一行的三种改法:不改 = 沿用控制台配置;删空 = 这个抽奖不做任何限制;改写 = 只覆盖写了的项(如
等级≥60 标识=龙王)。文本表达不了的口径(多标识判定、龙王口径)只在控制台配置、对所有抽奖动态生效
参与与查询
- 关键词加入:直接在群里发送抽奖的加入口令(消息内容需与口令完全一致)
加入抽奖 <编号>/退出抽奖 <编号>:指令式参与与退出;重复加入、未加入退出都会给出对应提示- 开奖后名单即固定:已经开奖的抽奖不能再加入、也不能退出(口令与指令两条路径一致拦截)
- 不满足参与条件时会回复具体原因(当前等级、上次发言距今天数、缺少哪个标识、数据暂不可用)
抽奖列表 [平台] [频道id]:列出本频道的抽奖(进行中 / 已结束),带上平台与频道 id 可跨频道查询抽奖详情 <编号>:标题、截止时间、描述、当前生效的参与条件、口令、奖品、参与名单;已开奖的抽奖附中奖名单抽奖成员 <编号>:参与名单(头像 + 昵称 + QQ 号,图片版最多显示render.memberLimit人,超出会提示剩余人数)
开奖与删除
开奖 [编号]:不填编号时会列出你创建的、尚未开奖的抽奖,回复编号即可开奖- 参与人数少于奖品数时:未开启「允许重复中奖」的抽奖只抽出与参与人数相同的名额(剩余奖品不发放,不会同一个人重复拿走多个奖品)
- 到点自动开奖:创建时填了开奖时间的抽奖会自动开奖,并把结果广播到抽奖所在的全部频道
- 开奖结果 = 一条带真实
@中奖者的文字行 + 结果图片(开图时仍保留文字,图片叫不动人) 删除抽奖 <编号>:删除抽奖(同时清理参与名单与已排定的提醒)- 开奖后记录按
cacheHours(默认 72 小时)保留,到期自动清理
开奖提醒
提醒由控制台配置驱动,配置是一张按剩余时长分档的规则表:每行 = 「时长上限」+「提醒位置(剩余时长的百分比)」。
默认规则:
| 时长上限 maxDuration | 提醒位置 percent | 含义 |
| --- | --- | --- |
| 1h | 20 | 创建时距开奖 ≤1 小时:开奖前 20% 处提醒(1 小时场次 → 开奖前 12 分钟) |
| 5h | 15 | 1~5 小时:开奖前 15% 处(5 小时场次 → 开奖前 45 分钟) |
| 1d | 10 | 5 小时~1 天:开奖前 10% 处(1 天场次 → 开奖前约 2.4 小时) |
| 0(不限) | 10 | 1 天以上:开奖前 10% 处(7 天场次 → 开奖前约 16.8 小时) |
- 一个抽奖只命中一行(上限最小且不小于自身时长的那行)→ 默认每场只提醒一次;提醒时机随时长自动缩放
maxDuration支持30m/1h/5h/1d/7d(纯数字 = 分钟),0或留空 = 不限(兜底行)percent填 1~99;同一行写多个(如20,10)即多次提醒- 到点在抽奖所在的全部频道播报倒计时(按频道语言本地化)
- 提醒任务随抽奖生命周期自动清理:开奖、删除、记录过期时一并取消;机器人重启时按当前配置重建
- 只对填了开奖时间的抽奖生效;整张表清空 = 不提醒
设置与帮助
时区 [偏移]:查看 / 修改时区,-c只改本频道、-d改默认值(影响开奖时间与提醒的本地时间显示)语言 [语言]:查看 / 修改语言偏好,-c只改本频道、-d改默认值频道id:显示当前频道 id(跨频道查询抽奖或排查绑定时用)抽奖帮助/ 直接发抽奖:输出指令一览
管理员诊断
抽奖接口诊断:逐个打印 Cookie、NapCat 群荣誉接口、QQ 网页荣誉接口的可用性与返回条数,便于定位「取不到数据」的原因抽奖成员诊断 [用户]:打印某个群成员的level/last_sent_time/join_time/role- 两者都会把完整原始返回写入插件日志,群里只显示摘要
配置
| 组 | 键 | 说明 |
| --- | --- | --- |
| basic | cacheHours | 开奖后抽奖记录的保留时长(小时),默认 72 |
| | defaultTimeOffset | 未设置时区的用户默认使用的时区偏移,默认 +8 |
| permission | authorityCreate | 创建抽奖所需的最低权限等级,默认 1 |
| | authorityManage | 管理他人抽奖(删除 / 手动开奖)所需的最低权限等级,默认 3 |
| | allowGuildAdminDelete | 群主与群管理员是否可删除本群抽奖,默认开启 |
| | allowGuildAdminEnd | 群主与群管理员是否可对本群抽奖手动开奖,默认开启 |
| join | minGroupLevel | 参与所需最低群聊等级(0 = 不限) |
| | minActiveDays | 最近 N 天内需发过言(0 = 不限) |
| | minContinuousDays | 最长连续发言天数下限(0 = 不限,建议 ≥7) |
| | requiredHonors | 需持有的群互动标识:群聊之火 / 群聊炽焰 / 龙王(可多选) |
| | honorMode | 多标识判定:满足任一(默认)/ 必须全部 |
| | dragonScope | 「龙王」口径:昨日活跃榜(默认)/ 仅当前龙王 |
| | onFetchError | 取不到成员 / 荣誉数据时:放行(默认)/ 拒绝 |
| | cacheMinutes | 群荣誉数据的缓存时长(分钟),默认 5 |
| render | style | 图片卡片风格:default(Koishi 品牌蓝紫渐变)/ anime(二次元:樱花粉 + 薰衣草紫、圆润描边、表情与星光)/ avemujica(暗紫黑 + 玫红 + 哥特金、衬线字体、纹章装饰、罗马数字名次) |
| | banner | 卡片顶部头图(可选):http(s) / data: / file: URL 或本机绝对路径(自动转 file://),留空不显示 |
| | width | 卡片宽度(CSS px,320~900,默认 420):图片会按聊天窗口缩放,越窄字体显示越大 |
| | create | 创建成功后把抽奖内容渲染成图片(图片代替文字成功提示),默认开启 |
| | detail | 抽奖详情 用图片重新渲染抽奖卡片,默认开启 |
| | member | 抽奖成员 用图片渲染参与名单,默认开启 |
| | memberLimit | 图片里参与名单最多显示几人(1~100,默认 12),超出提示「…… 等共 X 人」 |
| | list | 抽奖列表 用图片渲染,默认开启 |
| | result | 开奖结果用图片渲染,默认开启 |
| | avatar | 开奖结果图片里显示中奖者头像与昵称,默认开启 |
| remind | rules | 开奖提醒规则表(见「开奖提醒」),默认 4 行:1h→20 / 5h→15 / 1d→10 / 0→10 |
图片渲染(可选)
装上并启用 koishi-plugin-puppeteer 后,下列消息会改用内置 HTML 模板渲染成图片:
| 消息 | 图片内容 | 渲染失败 / 未装 puppeteer 时 |
| --- | --- | --- |
| 创建成功(创建抽奖) | 标题、编号、开奖时间、描述、加入口令、奖品、参与条件(图片代替文字) | 回退为文字成功提示 |
| 抽奖详情 <编号> | 与创建时同一张卡片 + 状态、参与人数、参与名单;已开奖附中奖名单 | 回退为文字详情 |
| 抽奖成员 <编号> | 参与名单:头像 + 昵称 + QQ 号 | 回退为文字名单 |
| 抽奖列表 | 进行中 / 已结束状态、编号、标题、截止时间 | 回退为文字列表 |
| 开奖广播 | 开奖编号、中奖者(头像 + 昵称 + QQ 号)、每人获得的奖品 | 回退为完整文字消息 |
交互矩阵(是否出图只取决于「装了 puppeteer」+「对应开关」):
| 依赖 / 开关 | 创建结果 | 抽奖详情 | 抽奖成员 | 抽奖列表 | 开奖结果 |
| --- | --- | --- | --- | --- | --- |
| 未装(或未启用)puppeteer | 文字 | 文字 | 文字 | 文字 | 文字(含 @ 中奖者) |
| 装了 puppeteer 且 render.* 开启 | 图片 | 图片 | 图片 | 图片 | 文字(@ 中奖者)+ 图片 |
| 装了 puppeteer 但对应开关关闭 | 文字 | 文字 | 文字 | 文字 | 文字 |
设计取舍:
- 图片宽度 = 卡片宽度:渲染服务截取的是
body包围盒,因此body收缩到卡片大小,不铺满浏览器视口(否则截图会按视口宽度带上大片背景,且图片过宽导致群里字体很小);默认卡片宽420px,可用render.width调整 - 开奖消息始终保留文字部分:图片没法提醒到人,所以出图时仍会发一条带真实
@中奖者的文字行,图片附在后面 - 渲染失败只打一条
warn日志(含原因)并回退文字,不会影响开奖 / 列表本身的逻辑 - 图片里的标签随语言变化(简体中文 / English / Deutsch),用户输入(标题、昵称)会做 HTML 转义
- 中奖者与参与者头像:优先用 OneBot 用户资料里的
avatar,取不到就按 QQ 号拼 qlogo 地址;昵称取不到时退化为只显示 QQ 号,头像加载失败时显示昵称首字 - 参与名单有人数上限:头像行很高,人数一多图片会长到没法看,所以按
render.memberLimit(默认 12)截断并提示剩余人数 - 头图(
render.banner):可给卡片顶部加一张横幅图,三种风格通用;插件不内置任何第三方美术素材
参与条件
- 数据来源:OneBot 的
get_group_member_info(level群聊等级、last_sent_time最后发言)+ QQ 群荣誉接口(群聊之火 / 群聊炽焰 / 龙王,含连续天数) - 荣誉取数走插件自实现的新版网页接口(
qun.qq.com的honor_*接口 + CSRF 令牌bkn),并会自动探测可用的鉴权方式 - 取不到数据时按
onFetchError处理:放行(默认,跳过取不到的条件)或拒绝(提示稍后再试);荣誉结果按cacheMinutes缓存 - 每个抽奖可覆盖:详情里显示的是实际生效的条件;与全局等价的覆盖不会额外落库
- 连续天数门槛注意:QQ 的连续发言榜只收录连续 ≥7 天的人,门槛建议设在 7 以上,并配合「最近发过言」一起使用
权限模型
- 只使用 Koishi 原生权限等级(
0封禁 /1普通用户 /3管理员 /4超级管理员),可用admin user.authorize <等级>或控制台的用户页面调整 - 创建者始终可以管理自己的抽奖;群主 / 群管理员默认可以删除与手动开奖(两个开关可关)
- 管理类指令(
开奖/删除抽奖/时区/语言/ 诊断)按authorityManage判定 - 参与条件与权限无关:任何人都要满足条件才能加入
数据与事件
插件使用以下数据表(均由插件自动创建):
| 表 | 用途 |
| --- | --- |
| roll | 抽奖主体(编号 / 标题 / 描述 / 口令 / 开奖时间 / 是否自动开奖 / 是否已开奖 / 中奖人可否重复) |
| prize、roll_prize | 奖品与「抽奖 × 奖品」关联 |
| roll_creator | 抽奖创建者 |
| roll_member | 参与名单(按加入顺序);对 (roll_id, user_id) 建唯一索引(老库在表首次被访问时自动补建),同一抽奖里同一用户只会有一条记录 |
| roll_channel | 抽奖与频道的关联(决定广播范围) |
| user_prize | 中奖记录(用户 × 奖品 × 数量) |
| roll_policy | per-roll 参与条件覆盖 |
插件通过 Koishi 事件总线暴露以下生命周期事件(前缀 giveaway/):
| 事件 | 触发时机 |
| --- | --- |
| giveaway/roll-add | 创建抽奖(参数:session、roll、奖品列表、可选的 per-roll 参与条件) |
| giveaway/roll-end | 开奖(参数:抽奖 id) |
| giveaway/roll-expired | 抽奖记录到期清理(参数:抽奖 id) |
| giveaway/roll-join / giveaway/roll-quit | 用户加入 / 退出抽奖 |
| giveaway/roll-key-update | 加入口令缓存需要刷新 |
| giveaway/remind-broadcast | 开奖提醒到点播报(参数:抽奖 id) |
多语言
- 内置三种语言:简体中文(zh-CN)/ English(en-US)/ Deutsch(de-DE),群消息、指令一览、控制台配置说明、图片卡片标签、诊断输出都随语言切换
- 用户输入同样多语言:创建模板标签、参与条件写法(
等级≥40/Level>=40/Stufe>=40)、取消词(q/quit/cancel/取消/abbrechen) - 插件日志保留中文(只面向管理员)
开发
npm install
npm run build # tsc 生成 lib/*.d.ts + esbuild 打包 lib/index.js
npm test # 5 套离线用例:创建流程 / 参与条件 / 控制台表单 / 权限 / 三语
npm run test:shots # 额外跑真实 Chromium 截图验收(需 npm i -D puppeteer-core 并设置 CHROME)
npm run preview list avemujica # 渲染一张卡片预览到 test/previews/- 用例全部离线运行(打桩
database/assets/puppeteer),不需要真的连 QQ;CI 在 Node 20 / 22 上跑npm ci → build → test - 用例覆盖:模板创建与标签解析、取消路径、参与条件四条判定与拒绝理由、权限矩阵、控制台表单结构与默认值、三语 key / 占位符 / 元素标签一致性与「代码引用的文案键都存在」、提醒排期与生命周期
test:shots会逐张渲染 6 种卡片 × 3 套主题并断言「图片宽度 = 卡片宽度、两侧无大片背景」,属于可选的像素级验收
目录结构:
src/
command/ 指令(roll / basic / i18n)
listener/ 事件监听(roll / remind)
util/ 业务工具(joinPolicy、rollPolicy、render、renderTheme、remindPlan…)
locales/ 三语文案
test/ 离线用例与预览工具
docs/ 功能与交互总览许可证
MIT © firedrakeovo。派生自 Roll-Bot-Project/roll-bot(MIT),原始版权声明见 LICENSE。
