自定义合成图片的 Mirai 插件 / 独立程序 / gocq-http插件, 灵感/部分数据来自 nonebot-plugin-petpet。
原生 java 编写, kotlin仅用于数据序列化, 使用底层API, 多线程优化: 轻量, 高性能, 易拓展
Mirai插件
- 
部署 Mirai 机器人框架 
- 
下载 最新版本 
- 
将插件放入 Mirai/plugins/
- 
下载 图片素材 
- 
将图片素材放入 Mirai/data/xmmt.dituon.petpet/
- 
启动 Mirai, 可自行更改配置文件Petpet.yml, 重启后生效 (参考 配置项说明)
- 使用 戳一戳 有 30%的概率触发; 或发送pet @xxx
pet key @xxx或key @xxx可返回指定图片 例如pet kiss @xxxkiss @xxx
可通过发送的图片生成Petpet
kiss [图片], 支持GIF可通过回复构造图片, 例如
[图片]->[回复[图片]] 对称
可使用
pet指令 获取keyList
- 
下载 最新版本 petpet.jar或petpet-no-ws.jar
- 
下载 图片素材 
- 
将图片素材放入 ./data/xmmt.dituon.petpet/目录
- 
cd ./java -jar petpet.jar, 可自行更改配置文件config.json, 重启后生效
- 
参考 WebServer一节 发起网络请求
Warning
此功能处于测试阶段, 目前仅能通过
key生成图片, 请期待后续开发!
- 
部署 gocq-http 机器人框架, 设置正向 WebSocket 监听 (默认端口为 8080)
- 
更改 gocq-http配置项message.post-format为array
- 
下载 最新版本 petpet.jar
- 
下载 图片素材 
- 
将图片素材放入 ./data/xmmt.dituon.petpet/目录
- 
cd ./java -jar petpet.jar, 可自行更改配置文件gocqConfig.json, 重启后生效
展开/收起
- command: pet
触发petpet指令, 默认为
pet例:
pet @xxxpet kiss @xxx仅发送
pet时会返回keyList
- probability: 30
戳一戳 触发概率,
0-100整数, 默认为30%
- antialias: true
画布抗锯齿, 默认为
true
- disabled: []
禁用表列, 默认为空, 在此数组中的
key不会被随机触发 (会覆盖data.json中的配置)
- keyCommandHead: ''
key作为指令头时的前缀, 默认为空例 (配置项为
'#'时):#kiss @xxxosu hso!
- respondReply: true
响应回复的消息, 默认为
true可通过回复消息 定位到之前发送的图片并构造petpet
启用后 会缓存接收到的图片(见
cachePoolSize)例 :
[回复[图片]]kiss(等价于kiss [图片])
- cachePoolSize: 10000
respondReply=true时, 图片消息缓存池大小, 默认为10000本质为
HashMap<imageId(long), imageUrl(String)>, 超过此限制会清空Map
- respondSelfNudge: false
某些情况下, 机器人会主动戳其他成员, 响应机器人自己发出的戳一戳, 默认为
false
- keyListFormat: FORWARD
发送
pet时keyList响应格式, 默认为FORWARD枚举:
MESSAGE(发送普通消息)FORWARD(发送转发消息)IMAGE(发送图片)
- disablePolicy: FULL
发送
pet on/off时 禁用哪些功能, 默认为FULL枚举:
NONE(无效)NUDGE(只禁用戳一戳)MESSAGE(只禁用指令)FULL(同时禁用戳一戳和指令)
- fuzzy: false
模糊匹配用户名, 默认为
false例 : (配置项为
true时):kiss @田所浩二(响应)kiss 浩二(响应)
- strictCommand: true
严格匹配指令, 默认为
true
人话: 可以省略key后的空格例 : (配置项为
false时):kiss 田所(响应)kiss田所(响应)
- synchronized: false
消息事件同步锁, 会锁住相同的消息事件, 默认为
false
人话: 多机器人对于同一条指令只有一个会响应
- gifEncoder: ANIMATED_LIB
GIF编码器, 默认为
ANIMATED_LIB枚举:
BUFFERED_STREAM: 基于缓存的STREAM流, 在编码过程中对Gif进行压缩;
- 编码速度较慢, 所需堆内存小, 生成Gif体积小
ANIMATED_LIB: 基于byte[]序列, 使用多线程分析像素;
- 编码速度极快, 所需堆内存较多, 生成Gif体积较小
SQUAREUP_LIB: 基于int[][]数组, 编码时不使用awt库, Gif压缩比低;
- 编码速度极慢, 所需堆内存多, 生成Gif体积较大
- gifMaxSize: []
GIF缩放阈值/尺寸, 默认为空 (不限制)
[width, height, frameLength]:当Gif长度超过
frameLength时, 会对Gif进行等比例缩放注: 缩放在图片合成时进行, 不会影响性能
例: (配置项为
-200 -200 -32]时)
- 当Gif长度超过
32帧时, 检查Gif尺寸- 当Gif尺寸大于
200*200时, 对Gif进行等比例缩放- Gif缩放后 最长边不会超过设定值 (当Gif中包含
40帧, 尺寸为300*500时)- 输出的Gif长度不变, 尺寸为
120*200
- gifQuality: 10
Gif编码质量(
1-99), 默认为10数字越小, 速度越慢, 质量越好 (大于
20时, 速度不会有明显提升)仅适用于
ANIMATED_LIB编码器
- headless: true
启用
headless模式, 默认为true
人话: 有些服务器没有输入输出设备, 画图库无法正常运行, 启用这个配置项可以修复, 因为总是有人不看常见问题, 干脆默认启用了(
- autoUpdate: true
自动更新
PetData, 每次启动时都会检查并自动下载船新pet, 默认为true注: 仅更新
PetData, 不会更新插件版本, 请放心食用
人话: 每次启动都会自动下载新的超赞梗图, 墙裂推荐
- repositoryUrl: 'https://dituon.github.io/petpet'
仓库地址, 用于自动更新, 默认为此仓库的
github page
- devMode: false
开发模式, 启用后任何人都能使用
pet reload指令热重载PetData, 默认为false
- messageHook: false
消息注入, 参考MessageHook, 默认为
false
- coolDown: 10
成功触发指令后对该用户的冷却时间(单位为秒), 默认为
1设置为
-1可禁用冷却
- groupCoolDown: -1
成功触发指令后对该群聊的冷却时间, 默认为
1
- inCoolDownMessage: 技能冷却中...
在冷却时间中触发命令的回复消息
配置项为
[nudge]时, 会以戳一戳形式回复
修改后重启 Mirai 以重新加载
群主或管理员使用
pet onpet off以 启用/禁用 戳一戳
pet on/off指令控制的事件可在配置文件中更改
可在配置文件中禁用指定key, 被禁用的key不会随机触发, 但仍可以通过指令使用
图片按key排序(见data/xmmt.dituon.petpet/)
展开/收起
..more&more
./data/xmmt.dituon.petpet/ 下的目录名为 key ,插件启动时会遍历 ./data/xmmt.dituon.petpet/$key/data.json
data.json 标准如下 (以 thump/data.json 为例)
// *为必须参数
{
  "type": "GIF", // 图片类型(enum)*
  "avatar": [{ // 头像(avatarObj[])*, 详见下文
      "type": "TO",
      "pos": [
        [65, 128, 77, 72], [67, 128, 73, 72], [54, 139, 94, 61], [57, 135, 86, 65]
      ],
      "round": true,
      "avatarOnTop": false
    }],
  "text": [], // 文字(textObj[])*, 详见下文
  "inRandomList": false, // 在随机列表中(bolean)
  "reverse": false, // GIF倒放(bolean)
  "delay": 50, // 帧间延时(ms/int), 默认为65
  "background": {}, // 背景(obj), 详见下文
  "alias": [ "别名1", "别名2" ], // 别名(str[])
  "hidden": false // 隐藏(bolean)
}
部分配置项设计参考了CSS标准, 并尽可能实现CSS渲染效果
type
- GIF动图
- IMG静态图片
坐标的基本组成单位是 4长度 int[] 数组
其中,前两项为 左上角顶点坐标, 后两项为 宽度和高度
例:
[65, 128, 77, 72] 即 头像的左上角顶点坐标是 (65,128), 宽度为 77, 高度为 72
如果是 GIF 类型,坐标应为二维数组,GIF 的每一帧视为单个图像文件
"pos": [ // pos的元素对应GIF的4帧
    [65, 128, 77, 72], [67, 128, 73, 72], [54, 139, 94, 61], [57, 135, 86, 65]
  ],
如果是IMG类型, 可以使用一维数组
  "pos": [0, 0, 200, 200]
4.0版本后, 坐标支持变量运算, 例如[100,100,"width/2","height*1.5^2"]
坐标格式枚举posType
- ZOOM缩放(见上文)
- DEFORM变形
DEFORM 坐标格式为 [[x1,y1],[x2,y2],[x3,y3],[x4,y4],[x_anchor,y_anchor]];
分别对应图片的[[左上角],[左下角],[右下角],[右上角],[锚点]],四角坐标用相对于锚点的偏移量表示
目前仿射变换仅支持单帧
3.0版本后 提供了更灵活的头像构造方法, 与之前的版本有很大差别
"avatar": [
    {
      "type": "FROM", //头像类型枚举(enum), 非空
      "pos": [[92, 64, 40, 40], [135, 40, 40, 40], [84, 105, 40, 40]], // 坐标
      "round": true, // 值为true时, 头像裁切为圆形, 默认为false
      "avatarOnTop": true, // 值为true时, 头像图层在背景之上, 默认为true
      "angle": 90, // 初始角度
      "opacity": 0.5 // 不透明度
    },
    {
      "type": "TO", 
      "pos": [[5, 8], [60, 90], [50, 90], [50, 0], [60, 120]],
      "posType": "DEFORM", // 图像变形 坐标格式, 默认为ZOOM
      "antialias": true, // 抗锯齿, 对头像单独使用抗锯齿算法, 默认为false
      "rotate": false // 值为true时, GIF类型的头像会旋转, 默认为false
    },
    {
      "type": "GROUP", 
      "pos": [[182, 64, "width/2", "height*1.5^2"], [225, 40, "40", 40], [174, 105, 40, "height+width"]], // 支持变量运算
      "crop": [0, 0, 50, 100], // 图片裁切坐标[x1, y1, x2, y2], 可简写为 [50, 100]
      "cropType": "PERCENT", // 裁切格式, 默认为NONE
      "style": [ // 风格化
        "MIRROR",
        "GRAY"
      ],
      "fit": "CONTAIN" // 填充模式, 默认为 FILL
    }
  ]
在
IMG中, 当rotate = true时, 头像会随机旋转角度,angle为最大值(angle = 0时, 随机范围为0-359)
头像类型枚举 type
- FROM发送者头像
- TO接收者头像, 或构造的图片
- GROUP群头像
- BOT机器人头像
- RANDOM随机头像 (随机从群聊成员中选择, 不会重复)
裁切格式枚举 cropType
- NONE不裁切
- PIXEL按像素裁切
- PERCENT按百分比裁切
风格化枚举 style
- MIRROR水平镜像
- FLIP上下翻转
- GRAY灰度化
- BINARIZATION二值化
填充模式 fit
- CONTAIN缩小以适应画布, 不改变原比例
- COVER裁切以适应画布, 不改变原比例
- FILL拉伸, 改变原比例
坐标变量
- width原图宽度
- height原图高度
如果你想在图片上添加文字,可以编辑 text
"text": [ // 这是一个数组, 可以添加很多文字
    {
      "text": "Petpet!", // 文字内容
      "color": "#66ccff", // 颜色, 默认为#191919
      "pos": [100, 100], // 坐标, 默认为 [2,14]
      "size": 24 // 字号, 默认为12
    },
    {
      "text": "发送者: $from, 接收者: $to", // 支持变量
      "color": [0,0,0,255], // 颜色可以使用RGB或RGBA的格式
      "pos": [20, 150], // 坐标
      "position": ["CENTER", "BOTTOM"], //坐标计算基准([x, y])
      "font": "宋体", // 字体, 默认为黑体
      "strokeColor": "#ffffff", // 描边颜色
      "strokeSize": 2 // 描边宽度
    },
    {
      "text": "$txt1[我]超市$txt2[你]!", // 支持关键词变量
      "pos": [0,200,300], // 第三个值为文本最大宽度
      "align": "CENTER", // 对齐方式, 默认为LEFT
      "wrap": "ZOOM", // 显示设置, 默认为NONE
      "style": "BLOD", // 字体样式, 默认为PLAIN
      "greedy": true // 贪婪匹配模式, 会匹配多余的关键词
    }
  ]
变量
- $from: 发送者, 会被替换为发送者群名片,如果没有群名片就替换为昵称
- $to: 接收者, 被戳或At的对象, 发送图片构造时为"你"
- $group: 群名称
- $txt(i)[(xxx)]: 文本变量, 可用于生成meme图, i为关键词索引, xxx为默认值; 例:- $txt1[我]超市$txt2[你]指令为- pet [key] 我 你
需要更多变量请提交 Issue
font
在data/fonts目录下的字体文件会注册到环境中
align
- LEFT: 左对齐
- RIGHT: 右对齐
- CENTER: 居中对齐
wrap
- NONE: 不换行
- BREAK: 自动换行
- ZOOM: 自动缩放
使用
BREAK或ZOOM时,maxWidth默认为200
style
- PLAIN: 默认
- BLOD: 粗体
- ITALIC: 斜体
position
- LEFT: 左定位(默认)
- RIGHT: 右定位
- TOP: 上定位(默认)
- BOTTOM: 下定位
- CENTER: 居中定位
4.0版本后, 支持动态创建画布
"background": {
    "size": ["avatar0Width*2","avatar0Height"], //支持变量运算
    "color": "#f0f0f0" //支持hex或rgba数组
  }
坐标变量
- avatar(i)Width- i号头像(- i为定义头像时的顺序, 从- 0开始)处理后的宽度
- avatar(i)Height- i号头像处理后的高度
- text(i)Width- i号文本渲染后的宽度
- text(i)Height- i号文本渲染后的高度
消息注入, 插件会检查将要发送的消息 解析后注入图片, 可配合各类消息回复插件使用
<pet></pet> 标签中的JSON会被解析, 请求格式参考 WebServer.POST
用例:
这段文字之后的标签会变成一张图片发送<pet>{
  "key": "petpet",
  "to": {
    "qq": 2544193782 
  },
  "textList": [
    "text1"
  ]
}</pet>消息的顺序会被正确处理, 支持多张图片
不同于 POST 请求格式, 你可以用 "qq" 令程序自动获取头像和昵称, 也可以自定义"name" "avatar"
(更推荐自定义的做法, 程序可能在某些情况下无法推断出正确的"name")
被
"hidden": true隐藏的模板会正常调用此功能默认禁用, 需在配置文件中启用
messageHook: true
Petpet 可以作为http服务器 / API单独运行, 被其它项目/语言使用
java -jar petpet.jar
启动时会生成 config.json:
{
  "port": 2333, // 监听端口
  "webServerThreadPoolSize": 10, // HTTP服务器线程池容量
  "dataPath": "data/xmmt.dituon.petpet", // PetData路径
  "gifMaxSize": [200, 200, 32], // GIF缩放阈值, 详见上文
  "gifEncoder": "ANIMATED_LIB", // GIF编码器, 详见上文
  "gifQuality": 100, // GIF质量, 详见上文
  "gifMakerThreadPoolSize": 0, // GIF编码器线程池容量, 详见上文
  "headless": true // 使用headless模式
}
程序使用com.sun.net.httpserver实现http服务器
访问 127.0.0.1:2333/petpet 以获取 PetDataList
使用 GET 传递参数, 例如 127.0.0.1:2333/petpet?key=petpet&toAvatar=$avatarUrl
127.0.0.1:2333/petpet?key=osu&textList=hso!
结构
展开/收起
- key(str): 对应- PetData,例如- kiss- rub
- fromAvatar- toAvatar- groupAvatar- botAvatar(url): 头像URL地址,- encodeURIComponent(rawUrl)
- randomAvatarList(url[]): 随机头像列表, 使用- ,分割多个url
- fromName- toName- groupName(str): 昵称, 有默认值
- textList(str): 根据空格分割此字符串, 作为额外数据
使用 POST 传递参数, 例如 127.0.0.1:2333/petpet
{
    "key": "petpet",
    "to": {
        "name":"d2n",
        "avatar":"https://q1.qlogo.cn/g?b=qq&nk=2544193782&s=640"
    },
    "randomAvatarList": [
        "url"
    ],
    "textList": [
        "text"
    ]
}
其中, key为必须项, 其它可以省略
可直接将图片二进制文件上传至服务器进行处理
类似于 GET数据结构, 使用 multipart/form-data
可参考
example-script中的代码实现请求
| 语言 | 示例 | 
|---|---|
| javascript | post.jsget.js | 
| python | example.py | 
| php | example.php | 
- 
戳一戳无法触发? 检查 Mirai 登录协议, 仅 ANDORID_PHONE可以收到 戳一戳 消息
- 
没有生成配置文件? Mirai 2.11.0提供了新的JavaAutoSaveConfig方法, 请更新Mirai版本至2.11.0(不是2.11.0-M1), 旧版本不支持自定义配置项
- 
Exception in coroutine <unnamed>?图片素材应位于 Mirai/data/xmmt.dituon.petpet目录下, 请检查路径
- 
Could not initialize class java.awt.Toolkit?对于无输入输出设备的服务器 需要启用 headless
- 
自动更新下载速度慢 / 无法连接远程资源? 修改 Petpet.yml中repositoryUrl的值为'https://ghproxy.com/https://raw.githubusercontent.com/Dituon/petpet/main'(高速镜像)
- 
自动更新后 读取 data.json出错?自动更新时网络出错导致, 删除出错的文件 重新获取即可 
- 
其它错误? 问题? 若此文档无法解决您的问题, 欢迎提交 issue
程序使用底层java.awt类合成图片, 渲染时使用多线程, 静态图片渲染时间一般不会超过1ms
对GIF编码器的分析, 转换, 映射部分进行多线程优化, 速度极快
Android JVM没有实现java.awt, 推荐使用JDK 11+版本
如果你想分享自定义的 Petpet, 欢迎Pr
程序提供超多实用API 拓展性极强, 附有互动式开发实例, 欢迎初学者学习!
- 
在别的项目二次开发: mirai-simplepetpet-plugin 
如果此插件和您预期的一样正常工作,请给我一个 star
欢迎提交任何请求
交流群: 828350277






































































