UNPKG

koishi-plugin-wheel-fortune

Version:

一个灵活可配置的转盘/轮盘抽奖 Koishi 插件。

176 lines (174 loc) 7.57 kB
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 });