koishi-plugin-ggcevo 1.3.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +3 -0
- package/lib/index.js +158 -142
- package/package.json +12 -2
- package/readme.md +24 -1
package/lib/index.d.ts
CHANGED
|
@@ -21,11 +21,14 @@ export interface Config {
|
|
|
21
21
|
handleInactiveUnbindDays: number;
|
|
22
22
|
/** 活动兑换管理员名字, 填写后兑换成功及兑换列表会提示向该管理员登记; 不填则不提醒 */
|
|
23
23
|
exchangeAdminName: string;
|
|
24
|
+
/** 咕咕之战菜单渲染方式: text=文字菜单(默认, 无需额外服务), image=图片菜单(需启用 puppeteer 服务) */
|
|
25
|
+
menuStyle: 'text' | 'image';
|
|
24
26
|
debugEnabled: boolean;
|
|
25
27
|
}
|
|
26
28
|
export declare const Config: Schema<Config>;
|
|
27
29
|
export declare const inject: {
|
|
28
30
|
required: string[];
|
|
31
|
+
optional: string[];
|
|
29
32
|
};
|
|
30
33
|
export declare const ItemConfig: Record<number, string>;
|
|
31
34
|
export declare const LotteryPoolConfig: Record<number, string>;
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
7
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
8
|
var __export = (target, all) => {
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -30,6 +40,10 @@ __export(src_exports, {
|
|
|
30
40
|
});
|
|
31
41
|
module.exports = __toCommonJS(src_exports);
|
|
32
42
|
var import_koishi = require("koishi");
|
|
43
|
+
var import_node_fs = __toESM(require("node:fs"));
|
|
44
|
+
var import_node_path = __toESM(require("node:path"));
|
|
45
|
+
var import_node_crypto = require("node:crypto");
|
|
46
|
+
var import_promises = require("node:fs/promises");
|
|
33
47
|
var name = "ggcevo";
|
|
34
48
|
var Config = import_koishi.Schema.intersect([
|
|
35
49
|
import_koishi.Schema.object({
|
|
@@ -61,12 +75,19 @@ var Config = import_koishi.Schema.intersect([
|
|
|
61
75
|
handleInactiveUnbindEnabled: import_koishi.Schema.boolean().description("是否启用句柄长时间未使用自动解绑").default(false),
|
|
62
76
|
handleInactiveUnbindDays: import_koishi.Schema.number().description("句柄超过该天数未使用将自动解绑(仅对非当前使用的句柄生效)").default(90).min(1)
|
|
63
77
|
}).description("句柄自动解绑"),
|
|
78
|
+
import_koishi.Schema.object({
|
|
79
|
+
menuStyle: import_koishi.Schema.union([
|
|
80
|
+
import_koishi.Schema.const("text").description("文字菜单(默认, 无需额外服务)"),
|
|
81
|
+
import_koishi.Schema.const("image").description("图片菜单(需安装并启用 koishi-plugin-puppeteer 或 @shangxueink/puppeteer-without-canvas)")
|
|
82
|
+
]).description("咕咕之战菜单渲染方式").default("text")
|
|
83
|
+
}).description("菜单设置"),
|
|
64
84
|
import_koishi.Schema.object({
|
|
65
85
|
debugEnabled: import_koishi.Schema.boolean().description("是否启用调试模式(启用后, ggcevo 所有指令的触发/参数/执行结果/错误信息会输出到 Koishi 日志)").default(false)
|
|
66
86
|
}).description("调试模式")
|
|
67
87
|
]);
|
|
68
88
|
var inject = {
|
|
69
|
-
required: ["database"]
|
|
89
|
+
required: ["database"],
|
|
90
|
+
optional: ["puppeteer"]
|
|
70
91
|
};
|
|
71
92
|
var ItemConfig = {
|
|
72
93
|
1: "金币",
|
|
@@ -2416,10 +2437,10 @@ ${rewardLines.join("\n")}` : "";
|
|
|
2416
2437
|
return record || null;
|
|
2417
2438
|
}
|
|
2418
2439
|
}, "getValidDocsToken");
|
|
2419
|
-
const docsApiRequest = /* @__PURE__ */ __name(async (method,
|
|
2440
|
+
const docsApiRequest = /* @__PURE__ */ __name(async (method, path2, data) => {
|
|
2420
2441
|
const token = await getValidDocsToken();
|
|
2421
2442
|
if (!token) throw new Error("腾讯文档应用级账号不可用, 请检查插件配置(Client ID / Client Secret)");
|
|
2422
|
-
const url = `https://docs.qq.com/openapi${
|
|
2443
|
+
const url = `https://docs.qq.com/openapi${path2}`;
|
|
2423
2444
|
const headers = {
|
|
2424
2445
|
"Access-Token": token.access_token,
|
|
2425
2446
|
"Client-Id": config.tencentDocsClientId,
|
|
@@ -2812,186 +2833,181 @@ ${rewardLines.join("\n")}` : "";
|
|
|
2812
2833
|
}
|
|
2813
2834
|
return "已退出封禁记录查询。";
|
|
2814
2835
|
});
|
|
2815
|
-
const MENU_PREFIX = "ggg:";
|
|
2816
2836
|
const menuCategories = [
|
|
2817
2837
|
{
|
|
2818
2838
|
key: "daily",
|
|
2819
|
-
label: "
|
|
2839
|
+
label: "每日玩法",
|
|
2840
|
+
emoji: "📅",
|
|
2820
2841
|
desc: "签到 / 挖矿 / 探索 / 补签",
|
|
2821
2842
|
items: [
|
|
2822
|
-
{ key: "sign", label: "签到", desc: "每日签到, 获取金币与咕咕币"
|
|
2823
|
-
{ key: "mine", label: "挖矿", desc: "领取挂机挖矿收益(每半小时4金币)"
|
|
2824
|
-
{ key: "explore", label: "探索", desc: "选择星系进行12小时探索"
|
|
2825
|
-
{ key: "makeup",
|
|
2843
|
+
{ key: "sign", icon: "签", label: "签到", desc: "每日签到, 获取金币与咕咕币" },
|
|
2844
|
+
{ key: "mine", icon: "挖", label: "挖矿", desc: "领取挂机挖矿收益(每半小时4金币)" },
|
|
2845
|
+
{ key: "explore", icon: "探", label: "探索", desc: "选择星系进行12小时探索" },
|
|
2846
|
+
{ key: "makeup", icon: "补", label: "补签", desc: "使用补签券补签漏签日期" }
|
|
2826
2847
|
]
|
|
2827
2848
|
},
|
|
2828
2849
|
{
|
|
2829
2850
|
key: "bag",
|
|
2830
|
-
label: "
|
|
2851
|
+
label: "物品背包",
|
|
2852
|
+
emoji: "🎒",
|
|
2831
2853
|
desc: "背包 / 个人信息 / 使用",
|
|
2832
2854
|
items: [
|
|
2833
|
-
{ key: "inv",
|
|
2834
|
-
{ key: "profile",
|
|
2835
|
-
{ key: "use", label: "使用 <名称>", desc: "使用指定物品(如赎罪券等)"
|
|
2855
|
+
{ key: "inv", icon: "包", label: "背包", desc: "查看自己的物品背包" },
|
|
2856
|
+
{ key: "profile", icon: "信", label: "个人信息", desc: "查看签到统计与个人信息" },
|
|
2857
|
+
{ key: "use", icon: "用", label: "使用 <名称>", desc: "使用指定物品(如赎罪券等)" }
|
|
2836
2858
|
]
|
|
2837
2859
|
},
|
|
2838
2860
|
{
|
|
2839
2861
|
key: "lottery",
|
|
2840
|
-
label: "
|
|
2862
|
+
label: "抽奖兑换",
|
|
2863
|
+
emoji: "🎲",
|
|
2841
2864
|
desc: "抽奖 / 抽奖概率 / 兑换 / 兑换列表",
|
|
2842
2865
|
items: [
|
|
2843
|
-
{ key: "lottery", label: "抽奖", desc: "抽奖, 可加选项 -p 奖池ID -c 次数"
|
|
2844
|
-
{ key: "odds",
|
|
2845
|
-
{ key: "exchange",
|
|
2846
|
-
{ key: "exchangeList",
|
|
2866
|
+
{ key: "lottery", icon: "抽", label: "抽奖", desc: "抽奖, 可加选项 -p 奖池ID -c 次数" },
|
|
2867
|
+
{ key: "odds", icon: "率", label: "抽奖概率", desc: "查看各奖池抽奖概率与保底说明" },
|
|
2868
|
+
{ key: "exchange", icon: "兑", label: "兑换 <名称>", desc: "使用兑换券兑换物品" },
|
|
2869
|
+
{ key: "exchangeList", icon: "表", label: "兑换列表", desc: "查看可兑换物品列表" }
|
|
2847
2870
|
]
|
|
2848
2871
|
},
|
|
2849
2872
|
{
|
|
2850
2873
|
key: "activity",
|
|
2851
|
-
label: "
|
|
2874
|
+
label: "活动",
|
|
2875
|
+
emoji: "🎉",
|
|
2852
2876
|
desc: "活动列表 / 领取活动",
|
|
2853
2877
|
items: [
|
|
2854
|
-
{ key: "actList", label: "活动列表", desc: '查看进行中的活动(参数"全部"查看全部)'
|
|
2855
|
-
{ key: "actClaim", label: "领取活动", desc: "领取指定(或最新)活动奖励"
|
|
2878
|
+
{ key: "actList", icon: "活", label: "活动列表", desc: '查看进行中的活动(参数"全部"查看全部)' },
|
|
2879
|
+
{ key: "actClaim", icon: "领", label: "领取活动", desc: "领取指定(或最新)活动奖励" }
|
|
2856
2880
|
]
|
|
2857
2881
|
},
|
|
2858
2882
|
{
|
|
2859
2883
|
key: "handle",
|
|
2860
|
-
label: "
|
|
2884
|
+
label: "句柄绑定",
|
|
2885
|
+
emoji: "🔗",
|
|
2861
2886
|
desc: "绑定 / 句柄 / 切换 / 查询 / 解绑 / 迁移",
|
|
2862
2887
|
items: [
|
|
2863
|
-
{ key: "bind", label: "绑定 <句柄>", desc: "绑定星际争霸2游戏句柄"
|
|
2864
|
-
{ key: "list",
|
|
2865
|
-
{ key: "switch",
|
|
2866
|
-
{ key: "check",
|
|
2867
|
-
{ key: "unbind",
|
|
2868
|
-
{ key: "migrate", label: "迁移 <QQ>", desc: "将指定QQ号绑定的句柄迁移到当前账号"
|
|
2888
|
+
{ key: "bind", icon: "绑", label: "绑定 <句柄>", desc: "绑定星际争霸2游戏句柄" },
|
|
2889
|
+
{ key: "list", icon: "句", label: "句柄", desc: "查询已绑定的游戏句柄" },
|
|
2890
|
+
{ key: "switch", icon: "切", label: "切换", desc: "切换正在使用的游戏句柄" },
|
|
2891
|
+
{ key: "check", icon: "查", label: "查询", desc: "查询某句柄是否已被绑定" },
|
|
2892
|
+
{ key: "unbind", icon: "解", label: "解绑", desc: "解除绑定某个游戏句柄" },
|
|
2893
|
+
{ key: "migrate", icon: "迁", label: "迁移 <QQ>", desc: "将指定QQ号绑定的句柄迁移到当前账号" }
|
|
2869
2894
|
]
|
|
2870
2895
|
},
|
|
2871
2896
|
{
|
|
2872
2897
|
key: "query",
|
|
2873
|
-
label: "
|
|
2898
|
+
label: "查询信息",
|
|
2899
|
+
emoji: "📊",
|
|
2874
2900
|
desc: "地图检测 / 封禁记录 / 签到奖励",
|
|
2875
2901
|
items: [
|
|
2876
|
-
{ key: "map",
|
|
2877
|
-
{ key: "ban",
|
|
2878
|
-
{ key: "reward",
|
|
2902
|
+
{ key: "map", icon: "图", label: "地图检测", desc: "查询已配置地图的检测状态" },
|
|
2903
|
+
{ key: "ban", icon: "封", label: "封禁记录", desc: "查询句柄的封禁记录" },
|
|
2904
|
+
{ key: "reward", icon: "奖", label: "签到奖励", desc: "查看签到奖励规则说明" }
|
|
2879
2905
|
]
|
|
2880
2906
|
}
|
|
2881
2907
|
];
|
|
2882
|
-
const
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
}
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
""
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
}, "sendMenu");
|
|
2958
|
-
const runMenuCommand = /* @__PURE__ */ __name(async (session, name2, args = []) => {
|
|
2959
|
-
session.messageId = "";
|
|
2960
|
-
await session.execute({ name: name2, args, options: {} });
|
|
2961
|
-
}, "runMenuCommand");
|
|
2962
|
-
ctx.command("咕咕之战").alias("菜单").usage("生成咕咕之战内容分类菜单(QQ 官方机器人支持按钮点击使用)").action(async (argv) => {
|
|
2963
|
-
await sendMenu(argv.session, void 0);
|
|
2964
|
-
});
|
|
2965
|
-
ctx.on("interaction/button", async (session) => {
|
|
2966
|
-
const button = session.event?.button;
|
|
2967
|
-
if (!button || typeof button.id !== "string" || !button.id.startsWith(MENU_PREFIX)) return;
|
|
2968
|
-
const parts = button.id.split(":");
|
|
2908
|
+
const buildTextMenu = /* @__PURE__ */ __name(() => [
|
|
2909
|
+
"🐤 咕咕之战菜单",
|
|
2910
|
+
"",
|
|
2911
|
+
...menuCategories.map((cat) => `${cat.emoji} ${cat.label}(${cat.desc})
|
|
2912
|
+
${cat.items.map((item) => item.label).join(" / ")}`),
|
|
2913
|
+
"",
|
|
2914
|
+
"提示: 直接输入上方指令名即可使用对应功能。"
|
|
2915
|
+
].join("\n"), "buildTextMenu");
|
|
2916
|
+
const buildMenuHtml = /* @__PURE__ */ __name((title, subtitle) => {
|
|
2917
|
+
const palette = ["#4a7dff", "#9b59b6", "#e67e22", "#27ae60", "#e74c3c", "#16a085", "#f39c12", "#8e44ad", "#2980b9", "#d35400", "#2ecc71", "#c0392b"];
|
|
2918
|
+
const groupHtml = menuCategories.map((group) => {
|
|
2919
|
+
const itemRows = group.items.map((item, i) => {
|
|
2920
|
+
const color = palette[i % palette.length];
|
|
2921
|
+
return `
|
|
2922
|
+
<div class="item">
|
|
2923
|
+
<div class="icon" style="background: ${color}">${item.icon}</div>
|
|
2924
|
+
<div class="info">
|
|
2925
|
+
<div class="name">${item.label}</div>
|
|
2926
|
+
<div class="desc">${item.desc}</div>
|
|
2927
|
+
</div>
|
|
2928
|
+
</div>`;
|
|
2929
|
+
}).join("");
|
|
2930
|
+
return `
|
|
2931
|
+
<div class="group">
|
|
2932
|
+
<div class="group-title">${group.label}</div>
|
|
2933
|
+
<div class="list">${itemRows}</div>
|
|
2934
|
+
</div>`;
|
|
2935
|
+
}).join("");
|
|
2936
|
+
return `<!DOCTYPE html>
|
|
2937
|
+
<html lang="zh">
|
|
2938
|
+
<head>
|
|
2939
|
+
<meta charset="UTF-8">
|
|
2940
|
+
<style>
|
|
2941
|
+
body { margin: 0; padding: 24px 28px; background:
|
|
2942
|
+
repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 2px),
|
|
2943
|
+
linear-gradient(135deg, #1b2a4a 0%, #0f1a33 100%);
|
|
2944
|
+
font-family: "Microsoft YaHei", "PingFang SC", sans-serif; color: #e8eefc; }
|
|
2945
|
+
.header { text-align: center; padding-bottom: 18px; border-bottom: 2px solid rgba(120,160,255,0.35); }
|
|
2946
|
+
.title { font-size: 32px; font-weight: bold; letter-spacing: 6px; color: #8fc0ff; text-shadow: 0 0 18px rgba(90,140,255,0.45); }
|
|
2947
|
+
.subtitle { margin-top: 8px; font-size: 14px; color: #93a6c8; }
|
|
2948
|
+
.group { margin-top: 22px; }
|
|
2949
|
+
.group-title { font-size: 15px; font-weight: bold; color: #ffd98a; letter-spacing: 3px; margin-bottom: 10px; padding-left: 10px; border-left: 3px solid #ffd98a; }
|
|
2950
|
+
.list { display: flex; flex-direction: column; gap: 12px; }
|
|
2951
|
+
.item { display: flex; align-items: center; gap: 14px; background: rgba(255,255,255,0.07); border: 1px solid rgba(140,170,255,0.22); border-radius: 12px; padding: 12px 16px; }
|
|
2952
|
+
.icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 19px; font-weight: bold; color: #fff; flex-shrink: 0; }
|
|
2953
|
+
.info { flex: 1; }
|
|
2954
|
+
.name { font-size: 20px; font-weight: bold; color: #ffffff; }
|
|
2955
|
+
.desc { margin-top: 4px; font-size: 13px; color: #9db1d8; line-height: 1.5; }
|
|
2956
|
+
.footer { margin-top: 24px; text-align: center; font-size: 12px; color: #6b7ea6; }
|
|
2957
|
+
</style>
|
|
2958
|
+
</head>
|
|
2959
|
+
<body>
|
|
2960
|
+
<div class="header">
|
|
2961
|
+
<div class="title">${title}</div>
|
|
2962
|
+
<div class="subtitle">${subtitle}</div>
|
|
2963
|
+
</div>
|
|
2964
|
+
${groupHtml}
|
|
2965
|
+
<div class="footer">输入上方指令名即可使用对应功能</div>
|
|
2966
|
+
</body>
|
|
2967
|
+
</html>`;
|
|
2968
|
+
}, "buildMenuHtml");
|
|
2969
|
+
const menuCacheDir = import_node_path.default.resolve(ctx.baseDir, "data/ggcevo/menu");
|
|
2970
|
+
const renderMenuImage = /* @__PURE__ */ __name(async (html) => {
|
|
2971
|
+
const logger = ctx.logger("ggcevo");
|
|
2972
|
+
const hash = (0, import_node_crypto.createHash)("sha256").update(html).digest("hex");
|
|
2973
|
+
const cachePath = import_node_path.default.resolve(menuCacheDir, `${hash}.jpg`);
|
|
2974
|
+
if (import_node_fs.default.existsSync(cachePath)) {
|
|
2975
|
+
const cached = await (0, import_promises.readFile)(cachePath);
|
|
2976
|
+
return import_koishi.h.image(`data:image/jpeg;base64,${cached.toString("base64")}`);
|
|
2977
|
+
}
|
|
2978
|
+
if (!ctx.puppeteer) {
|
|
2979
|
+
logger.warn("[menu-render] 未启用 puppeteer 服务");
|
|
2980
|
+
return "⚠️ 未启用 puppeteer 服务,无法渲染图片菜单。请安装并启用 `koishi-plugin-puppeteer` 或 `@shangxueink/puppeteer-without-canvas`。";
|
|
2981
|
+
}
|
|
2982
|
+
const page = await ctx.puppeteer.page();
|
|
2969
2983
|
try {
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
const
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
await session.send(`请输入${item.label}后重试。`);
|
|
2983
|
-
return;
|
|
2984
|
-
}
|
|
2985
|
-
await runMenuCommand(session, item.cmd, [value]);
|
|
2986
|
-
} else {
|
|
2987
|
-
await runMenuCommand(session, item.cmd);
|
|
2988
|
-
}
|
|
2984
|
+
await page.setViewport({ width: 1280, height: 100, deviceScaleFactor: 1 });
|
|
2985
|
+
await page.setContent(html);
|
|
2986
|
+
await page.waitForNetworkIdle();
|
|
2987
|
+
const img = await page.screenshot({ type: "jpeg", quality: 85, fullPage: true, encoding: "binary" });
|
|
2988
|
+
logger.info("[menu-render] 菜单图渲染完成, 体积 %s KB", (img.length / 1024).toFixed(1));
|
|
2989
|
+
try {
|
|
2990
|
+
await (0, import_promises.mkdir)(menuCacheDir, { recursive: true });
|
|
2991
|
+
const files = await (0, import_promises.readdir)(menuCacheDir);
|
|
2992
|
+
for (const file of files) await (0, import_promises.unlink)(import_node_path.default.resolve(menuCacheDir, file));
|
|
2993
|
+
await (0, import_promises.writeFile)(cachePath, img);
|
|
2994
|
+
} catch (e) {
|
|
2995
|
+
logger.error("[menu-render] 写入菜单图缓存失败: %o", e);
|
|
2989
2996
|
}
|
|
2997
|
+
return import_koishi.h.image(`data:image/jpeg;base64,${img.toString("base64")}`);
|
|
2990
2998
|
} catch (e) {
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2999
|
+
logger.warn("[menu-render] 渲染菜单图片失败: %o", e);
|
|
3000
|
+
return "⚠️ 菜单图片渲染失败,请查看日志后重试。";
|
|
3001
|
+
} finally {
|
|
3002
|
+
await page.close();
|
|
3003
|
+
}
|
|
3004
|
+
}, "renderMenuImage");
|
|
3005
|
+
ctx.command("咕咕之战", "查看咕咕之战内容分类菜单").alias("菜单").action(async ({ session }) => {
|
|
3006
|
+
if (config.menuStyle === "image") {
|
|
3007
|
+
const result = await renderMenuImage(buildMenuHtml("咕咕之战", "内容分类菜单 · 输入指令名即可使用"));
|
|
3008
|
+
return typeof result === "string" ? result : (0, import_koishi.h)("quote", { id: session.messageId }, result);
|
|
3009
|
+
}
|
|
3010
|
+
return buildTextMenu();
|
|
2995
3011
|
});
|
|
2996
3012
|
}
|
|
2997
3013
|
__name(apply, "apply");
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-ggcevo",
|
|
3
3
|
"description": "GGCEVO 综合插件:星际争霸2游戏句柄绑定 + 签到/抽奖/兑换/背包/活动系统等虚拟物品玩法",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.2",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -28,6 +28,16 @@
|
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"koishi": "^4.18.7"
|
|
31
|
+
"koishi": "^4.18.7",
|
|
32
|
+
"koishi-plugin-puppeteer": "^3.9.0",
|
|
33
|
+
"@shangxueink/puppeteer-without-canvas": "^3.0.7"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"koishi-plugin-puppeteer": {
|
|
37
|
+
"optional": true
|
|
38
|
+
},
|
|
39
|
+
"@shangxueink/puppeteer-without-canvas": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
32
42
|
}
|
|
33
43
|
}
|
package/readme.md
CHANGED
|
@@ -78,6 +78,12 @@ npm install koishi-plugin-ggcevo
|
|
|
78
78
|
- 功能上线前已绑定的旧句柄无时间记录,首次检查时以当前时间作为绑定时间补齐。
|
|
79
79
|
- 定时任务每天检查一次(启动后延迟 5 秒首次检查),删除超过阈值天数未使用且非当前使用的句柄。
|
|
80
80
|
|
|
81
|
+
### 菜单设置
|
|
82
|
+
|
|
83
|
+
| 配置项 | 类型 | 默认值 | 说明 |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
| `menuStyle` | `'text' \| 'image'` | `'text'` | 咕咕之战菜单渲染方式。`text`=文字菜单(默认,无需额外服务);`image`=图片菜单(需安装并启用 `koishi-plugin-puppeteer` 或 `@shangxueink/puppeteer-without-canvas` 其一,未启用时自动降级为文字菜单并提示) |
|
|
86
|
+
|
|
81
87
|
### 活动兑换
|
|
82
88
|
|
|
83
89
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
@@ -102,7 +108,7 @@ npm install koishi-plugin-ggcevo
|
|
|
102
108
|
|
|
103
109
|
| 指令 | 别名 | 权限 | 说明 |
|
|
104
110
|
| --- | --- | --- | --- |
|
|
105
|
-
| `咕咕之战` | 菜单 | — |
|
|
111
|
+
| `咕咕之战` | 菜单 | — | 生成咕咕之战内容分类菜单(「每日玩法」「物品背包」「抽奖兑换」「活动」「句柄绑定」「查询信息」六类,仅收录普通用户可用指令)。根据配置项 `menuStyle` 选择渲染方式:`text`(默认)输出文字分类菜单,`image` 使用 puppeteer 渲染分类图片菜单(需启用 puppeteer 服务,未启用时自动降级为文字菜单) |
|
|
106
112
|
| `签到` | — | — | 每日签到,获取奖励并发放签到券/补签券等。每月首次签到时,若签到者的游戏句柄匹配管理员福利文档记录,额外发放 50 咕咕币津贴(仅校验句柄) |
|
|
107
113
|
| `挖矿` | — | — | 领取挂机挖矿收益(咕咕之战玩法)。每半小时收益 4 金币,单次存储上限 24 小时,领取后立即进入下一轮挖矿。累计挖矿总时长与累计收益金币数会被记录统计。受宵禁影响:17:00-24:00 禁止领取 |
|
|
108
114
|
| `探索` | — | — | 选择星系进行 12 小时探索,结束后领取收益(咕咕之战玩法)。星系:天枢(成功率100%,成功80金币)、赤潮(成功率60%,成功120金币/失败60金币)、千帆(成功率80%,成功50金币/失败25金币,成功时独立概率获得补签券×1(10%)、咕咕币1-3枚(50%),失败无道具)。金币/道具发放到背包,按星系累计统计探索次数/成功次数/累计金币。受宵禁影响:17:00-24:00 禁止 |
|
|
@@ -173,6 +179,23 @@ npm install koishi-plugin-ggcevo
|
|
|
173
179
|
|
|
174
180
|
## 更新日志
|
|
175
181
|
|
|
182
|
+
### v1.3.2
|
|
183
|
+
|
|
184
|
+
图片菜单渲染对齐 preview-help 方案,新增磁盘缓存。
|
|
185
|
+
|
|
186
|
+
- **新增图片菜单磁盘缓存**:`咕咕之战` 图片菜单按 HTML 内容 hash 缓存到 `data/ggcevo/menu/`,命中缓存直接读回图片(免去重复渲染、无需 puppeteer 也可返回缓存图),渲染成功后会清理旧缓存文件
|
|
187
|
+
- **渲染参数对齐 preview-help**:viewport 由 900×DPR2 调整为 1280×DPR1,`setContent` 后改用 `waitForNetworkIdle()` 等待页面网络空闲
|
|
188
|
+
|
|
189
|
+
### v1.3.1
|
|
190
|
+
|
|
191
|
+
取消 Markdown 按钮菜单,改为可配置的文字/图片菜单。
|
|
192
|
+
|
|
193
|
+
- **取消 Markdown 按钮**:移除 `咕咕之战`(别名 `菜单`)的 QQ 官方机器人 Markdown + 按钮渲染及按钮回调处理,改为输出分类菜单(「每日玩法」「物品背包」「抽奖兑换」「活动」「句柄绑定」「查询信息」六类)
|
|
194
|
+
- **新增配置项 `menuStyle`**(默认 `text`):选择菜单渲染方式
|
|
195
|
+
- `text`:文字分类菜单,无需任何额外服务(默认)
|
|
196
|
+
- `image`:图片菜单,使用 puppeteer 将 HTML 渲染为图片后以 base64 data URL 发送(与 preview-help 插件同方案)
|
|
197
|
+
- **可选依赖 puppeteer 服务**:声明 `koishi-plugin-puppeteer`(^3.9.0)与 `@shangxueink/puppeteer-without-canvas`(^3.0.7)为可选 peer 依赖,任选其一安装启用即可渲染图片菜单;`image` 模式未启用 puppeteer 服务时自动降级为文字菜单并提示
|
|
198
|
+
|
|
176
199
|
### v1.3.0
|
|
177
200
|
|
|
178
201
|
咕咕之战菜单改为 QQ 官方机器人 Markdown 按钮菜单(取代 v1.2.0 移除的图片渲染方案,不再依赖 puppeteer)。
|