koishi-plugin-wheel-fortune
Version:
一个灵活可配置的转盘/轮盘抽奖 Koishi 插件。
176 lines (174 loc) • 7.57 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name2 in all)
__defProp(target, name2, { get: all[name2], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
apply: () => apply,
name: () => name,
schema: () => schema,
using: () => using
});
module.exports = __toCommonJS(src_exports);
var import_koishi = require("koishi");
var name = "fortune-wheel";
var using = ["puppeteer"];
var schema = import_koishi.Schema.object({
turntables: import_koishi.Schema.array(import_koishi.Schema.object({
name: import_koishi.Schema.string().description("转盘的名称,方便辨认。").required(),
command: import_koishi.Schema.string().description("触发此转盘的指令(无需加/)。").required(),
options: import_koishi.Schema.array(String).description("转盘上的选项列表。").required()
})).description("自定义转盘列表。").default([
{
name: "今天吃什么",
command: "吃饭转盘",
options: ["面条", "自己做", "麻辣烫", "肯德基", "麦当劳", "炒饭", "火锅", "烧烤", "日料", "螺蛳粉", "饺子", "黄焖鸡"]
},
{
name: "周末做什么",
command: "周末转盘",
options: ["看电影", "打游戏", "出门逛街", "学习", "睡觉", "大扫除"]
}
]),
spinningGifUrl: import_koishi.Schema.string().description("“转盘正在旋转时”显示的 GIF 图片地址。").default("https://s1.aigei.com/src/img/gif/26/267520cf0a4342cf9a4950e439a84ca0.gif?imageMogr2/auto-orient/thumbnail/!282x282r/gravity/Center/crop/282x282/quality/85/%7CimageView2/2/w/282&e=2051020800&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:asY2pQU8qnC3Nz1UVoWT1TszGY4="),
delay: import_koishi.Schema.number().min(1).max(10).default(3).description("从发送旋转动画到揭晓结果的延迟(秒)。")
});
function apply(ctx, config) {
async function drawTurntableWithCanvas(options, resultIndex) {
let page;
try {
const width = 500, height = 500;
const html = `
<html>
<head><style>body { margin: 0; }</style></head>
<body><canvas id="turntable-canvas" width="${width}" height="${height}"></canvas></body>
</html>`;
page = await ctx.puppeteer.page();
await page.setViewport({ width, height });
await page.setContent(html, { waitUntil: "networkidle0" });
await page.evaluate((options2, resultIndex2) => {
const canvas = document.getElementById("turntable-canvas");
const context = canvas.getContext("2d");
const width2 = canvas.width, height2 = canvas.height;
const centerX = width2 / 2, centerY = height2 / 2;
const radius = 240;
context.fillStyle = "#fff";
context.fillRect(0, 0, width2, height2);
const numOptions = options2.length;
const arc = 2 * Math.PI / numOptions;
const targetAngle = 1.5 * Math.PI;
const resultSectorCenterAngle = resultIndex2 * arc + arc / 2;
const rotationAngle = targetAngle - resultSectorCenterAngle;
context.save();
context.translate(centerX, centerY);
context.rotate(rotationAngle);
const colors = ["#f8bbd0", "#fce4ec"];
for (let i = 0; i < numOptions; i++) {
const angle = i * arc;
const text = options2[i];
const textRadius = radius * 0.65;
context.beginPath();
context.moveTo(0, 0);
context.arc(0, 0, radius, angle, angle + arc);
context.closePath();
context.fillStyle = colors[i % 2];
context.fill();
context.save();
context.rotate(angle + arc / 2);
context.fillStyle = "#333";
context.font = "bold 24px sans-serif";
context.textAlign = "center";
context.textBaseline = "middle";
context.fillText(text, textRadius, 0);
context.restore();
}
context.restore();
context.beginPath();
context.arc(centerX, centerY, 35, 0, 2 * Math.PI);
context.fillStyle = "#fff";
context.fill();
context.strokeStyle = "#e91e63";
context.lineWidth = 4;
context.stroke();
const pointerSize = 5;
context.beginPath();
context.moveTo(centerX - pointerSize * 5, pointerSize * 2);
context.lineTo(centerX + pointerSize * 5, pointerSize * 2);
context.lineTo(centerX, 50);
context.closePath();
context.fillStyle = "#f57c00";
context.fill();
context.beginPath();
context.arc(centerX, centerY, radius, 0, 2 * Math.PI);
context.strokeStyle = "#e91e63";
context.lineWidth = 4;
context.stroke();
}, options, resultIndex);
const canvasElement = await page.$("#turntable-canvas");
if (!canvasElement) throw new Error("无法在 Puppeteer 页面中找到 canvas 元素");
const buffer = await canvasElement.screenshot({
type: "jpeg",
quality: 95
});
return buffer;
} finally {
if (page) await page.close();
}
}
__name(drawTurntableWithCanvas, "drawTurntableWithCanvas");
config.turntables.forEach((turntable) => {
if (!turntable.command || turntable.options.length === 0) return;
ctx.command(turntable.command, `启动「${turntable.name}」转盘`).action(async ({ session }) => {
try {
let gifBuffer;
try {
const gifArrayBuffer = await ctx.http.get(config.spinningGifUrl, { responseType: "arraybuffer" });
gifBuffer = Buffer.from(gifArrayBuffer);
} catch (error) {
ctx.logger("turntable").warn(`旋转 GIF 下载失败: ${error.message}`);
}
const initialMessage = [
(0, import_koishi.h)("p", `${turntable.name},启动!`),
gifBuffer ? import_koishi.h.image(gifBuffer, "image/gif") : (0, import_koishi.h)("p", "(旋转中...)")
];
await session.send(initialMessage);
await ctx.sleep(config.delay * 1e3);
const options = turntable.options;
const resultIndex = Math.floor(Math.random() * options.length);
const result = options[resultIndex];
const resultImageBuffer = await drawTurntableWithCanvas(options, resultIndex);
const finalMessage = [
(0, import_koishi.h)("p", `结果是... 【${result}】!`),
import_koishi.h.image(resultImageBuffer, "image/jpeg")
];
await session.send(finalMessage);
} catch (error) {
ctx.logger("turntable").error("转盘执行出错 (Canvas):", error);
return "转盘好像被外星人劫持了...";
}
});
});
}
__name(apply, "apply");
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
apply,
name,
schema,
using
});