koishi-plugin-ggcevo 1.1.20 → 1.3.1
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 +2 -0
- package/lib/index.js +166 -157
- package/package.json +1 -4
- package/readme.md +32 -3
package/lib/index.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ 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>;
|
package/lib/index.js
CHANGED
|
@@ -61,6 +61,12 @@ var Config = import_koishi.Schema.intersect([
|
|
|
61
61
|
handleInactiveUnbindEnabled: import_koishi.Schema.boolean().description("是否启用句柄长时间未使用自动解绑").default(false),
|
|
62
62
|
handleInactiveUnbindDays: import_koishi.Schema.number().description("句柄超过该天数未使用将自动解绑(仅对非当前使用的句柄生效)").default(90).min(1)
|
|
63
63
|
}).description("句柄自动解绑"),
|
|
64
|
+
import_koishi.Schema.object({
|
|
65
|
+
menuStyle: import_koishi.Schema.union([
|
|
66
|
+
import_koishi.Schema.const("text").description("文字菜单(默认, 无需额外服务)"),
|
|
67
|
+
import_koishi.Schema.const("image").description("图片菜单(需安装并启用 koishi-plugin-puppeteer 或 @shangxueink/puppeteer-without-canvas)")
|
|
68
|
+
]).description("咕咕之战菜单渲染方式").default("text")
|
|
69
|
+
}).description("菜单设置"),
|
|
64
70
|
import_koishi.Schema.object({
|
|
65
71
|
debugEnabled: import_koishi.Schema.boolean().description("是否启用调试模式(启用后, ggcevo 所有指令的触发/参数/执行结果/错误信息会输出到 Koishi 日志)").default(false)
|
|
66
72
|
}).description("调试模式")
|
|
@@ -1004,163 +1010,6 @@ ${rewardLines.join("\n")}` : "";
|
|
|
1004
1010
|
📊 该星系累计探索 ${newStat.total_count} 次,成功 ${newStat.success_count} 次,累计获得 ${newStat.total_coins} 金币
|
|
1005
1011
|
🛸 可再次使用 探索 命令选择星系开启新的探索。`;
|
|
1006
1012
|
});
|
|
1007
|
-
const renderMenuImage = /* @__PURE__ */ __name(async (html, session) => {
|
|
1008
|
-
const logger = ctx.logger("ggcevo");
|
|
1009
|
-
if (!ctx.puppeteer) {
|
|
1010
|
-
logger.warn("[menu-render] 未安装 puppeteer 服务, 降级为文本提示");
|
|
1011
|
-
return "⚠️ 未安装 puppeteer 服务,无法渲染菜单图片。请安装 `koishi-plugin-puppeteer` 或 `@shangxueink/puppeteer-without-canvas` 并启用其一。";
|
|
1012
|
-
}
|
|
1013
|
-
const page = await ctx.puppeteer.page();
|
|
1014
|
-
try {
|
|
1015
|
-
await page.setViewport({ width: 640, height: 800, deviceScaleFactor: 4 });
|
|
1016
|
-
logger.info("[menu-render] 页面已创建, 正在写入 HTML...");
|
|
1017
|
-
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
1018
|
-
logger.info("[menu-render] HTML 写入完成, 正在截图(PNG)...");
|
|
1019
|
-
const img = await page.screenshot({ type: "png", fullPage: true });
|
|
1020
|
-
const dataUrl = `data:image/png;base64,${img.toString("base64")}`;
|
|
1021
|
-
const sizeKB = (img.length / 1024).toFixed(1);
|
|
1022
|
-
logger.info("[menu-render] 截图完成(PNG), 体积 %s KB, 生成 data URL 长度 %d", sizeKB, dataUrl.length);
|
|
1023
|
-
if (session?.bot && session.platform === "qq") {
|
|
1024
|
-
const bot = session.bot;
|
|
1025
|
-
if (bot.config && bot.config.uploadThreshold !== 10 * 1024 * 1024) {
|
|
1026
|
-
bot.config.uploadThreshold = 10 * 1024 * 1024;
|
|
1027
|
-
logger.info("[menu-render] 已调整 QQ 上传阈值至 10MB, 菜单图改走普通上传通道");
|
|
1028
|
-
}
|
|
1029
|
-
if (config.debugEnabled) {
|
|
1030
|
-
try {
|
|
1031
|
-
const uploadPath = session.isDirect ? `/v2/users/${session.userId}/files` : `/v2/groups/${session.channelId}/files`;
|
|
1032
|
-
const uploadRes = await session.bot.http.post(uploadPath, {
|
|
1033
|
-
file_type: 1,
|
|
1034
|
-
file_data: img.toString("base64")
|
|
1035
|
-
});
|
|
1036
|
-
logger.info("[menu-render] QQ 上传探测成功: %o", uploadRes);
|
|
1037
|
-
} catch (e) {
|
|
1038
|
-
logger.warn("[menu-render] QQ 上传探测失败: %s | 响应: %o", e?.message, e?.response?.data);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
return import_koishi.h.image(dataUrl);
|
|
1043
|
-
} catch (e) {
|
|
1044
|
-
logger.warn("[menu-render] 渲染菜单图片失败: %o", e);
|
|
1045
|
-
return "⚠️ 菜单图片渲染失败,请查看日志。";
|
|
1046
|
-
} finally {
|
|
1047
|
-
await page.close();
|
|
1048
|
-
logger.info("[menu-render] 页面已关闭");
|
|
1049
|
-
}
|
|
1050
|
-
}, "renderMenuImage");
|
|
1051
|
-
const buildMenuHtml = /* @__PURE__ */ __name((title, subtitle, groups) => {
|
|
1052
|
-
const palette = ["#4a7dff", "#9b59b6", "#e67e22", "#27ae60", "#e74c3c", "#16a085", "#f39c12", "#8e44ad", "#2980b9", "#d35400", "#2ecc71", "#c0392b"];
|
|
1053
|
-
const groupHtml = groups.map((group) => {
|
|
1054
|
-
const itemRows = group.items.map((item, i) => {
|
|
1055
|
-
const color = palette[i % palette.length];
|
|
1056
|
-
return `
|
|
1057
|
-
<div class="item">
|
|
1058
|
-
<div class="icon" style="background: ${color}">${item.icon}</div>
|
|
1059
|
-
<div class="info">
|
|
1060
|
-
<div class="name">${item.name}</div>
|
|
1061
|
-
<div class="desc">${item.desc}</div>
|
|
1062
|
-
</div>
|
|
1063
|
-
</div>`;
|
|
1064
|
-
}).join("");
|
|
1065
|
-
return `
|
|
1066
|
-
<div class="group">
|
|
1067
|
-
<div class="group-title">${group.title}</div>
|
|
1068
|
-
<div class="list">${itemRows}</div>
|
|
1069
|
-
</div>`;
|
|
1070
|
-
}).join("");
|
|
1071
|
-
return `<!DOCTYPE html>
|
|
1072
|
-
<html lang="zh">
|
|
1073
|
-
<head>
|
|
1074
|
-
<meta charset="UTF-8">
|
|
1075
|
-
<style>
|
|
1076
|
-
body { margin: 0; padding: 24px 28px; background:
|
|
1077
|
-
repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 2px),
|
|
1078
|
-
linear-gradient(135deg, #1b2a4a 0%, #0f1a33 100%);
|
|
1079
|
-
font-family: "Microsoft YaHei", "PingFang SC", sans-serif; color: #e8eefc; }
|
|
1080
|
-
.header { text-align: center; padding-bottom: 18px; border-bottom: 2px solid rgba(120,160,255,0.35); }
|
|
1081
|
-
.title { font-size: 32px; font-weight: bold; letter-spacing: 6px; color: #8fc0ff; text-shadow: 0 0 18px rgba(90,140,255,0.45); }
|
|
1082
|
-
.subtitle { margin-top: 8px; font-size: 14px; color: #93a6c8; }
|
|
1083
|
-
.group { margin-top: 22px; }
|
|
1084
|
-
.group-title { font-size: 15px; font-weight: bold; color: #ffd98a; letter-spacing: 3px; margin-bottom: 10px; padding-left: 10px; border-left: 3px solid #ffd98a; }
|
|
1085
|
-
.list { display: flex; flex-direction: column; gap: 12px; }
|
|
1086
|
-
.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; }
|
|
1087
|
-
.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; }
|
|
1088
|
-
.info { flex: 1; }
|
|
1089
|
-
.name { font-size: 20px; font-weight: bold; color: #ffffff; }
|
|
1090
|
-
.desc { margin-top: 4px; font-size: 13px; color: #9db1d8; line-height: 1.5; }
|
|
1091
|
-
.footer { margin-top: 24px; text-align: center; font-size: 12px; color: #6b7ea6; }
|
|
1092
|
-
</style>
|
|
1093
|
-
</head>
|
|
1094
|
-
<body>
|
|
1095
|
-
<div class="header">
|
|
1096
|
-
<div class="title">${title}</div>
|
|
1097
|
-
<div class="subtitle">${subtitle}</div>
|
|
1098
|
-
</div>
|
|
1099
|
-
${groupHtml}
|
|
1100
|
-
<div class="footer">输入上方指令名即可使用对应功能</div>
|
|
1101
|
-
</body>
|
|
1102
|
-
</html>`;
|
|
1103
|
-
}, "buildMenuHtml");
|
|
1104
|
-
const gameMenuGroups = [
|
|
1105
|
-
{
|
|
1106
|
-
title: "每日玩法",
|
|
1107
|
-
items: [
|
|
1108
|
-
{ icon: "签", name: "签到", desc: "每日签到,获取签到券/补签券等奖励,每月首次签到可领津贴" },
|
|
1109
|
-
{ icon: "补", name: "补签", desc: "使用补签券补签漏签的日期" },
|
|
1110
|
-
{ icon: "抽", name: "抽奖", desc: "使用金币/咕咕币/兑换券参与抽奖,选项:-p 奖池ID -c 次数" },
|
|
1111
|
-
{ icon: "挖", name: "挖矿", desc: "挂机挖矿,每半小时收益4金币,上限24小时,领取后自动进入下一轮" },
|
|
1112
|
-
{ icon: "探", name: "探索", desc: "选择星系进行12小时探索,探索结束领取金币/道具收益" }
|
|
1113
|
-
]
|
|
1114
|
-
},
|
|
1115
|
-
{
|
|
1116
|
-
title: "物品与背包",
|
|
1117
|
-
items: [
|
|
1118
|
-
{ icon: "兑", name: "兑换列表", desc: "查看可兑换物品列表及兑换券消耗" },
|
|
1119
|
-
{ icon: "换", name: "兑换", desc: "使用兑换券兑换皮肤/宠物/入场特效/角色冠名权等物品" },
|
|
1120
|
-
{ icon: "包", name: "背包", desc: "查看自己背包中的物品" },
|
|
1121
|
-
{ icon: "用", name: "使用", desc: "使用指定物品(如赎罪券等)" },
|
|
1122
|
-
{ icon: "信", name: "个人信息", desc: "查看自己的签到统计与个人信息" }
|
|
1123
|
-
]
|
|
1124
|
-
},
|
|
1125
|
-
{
|
|
1126
|
-
title: "活动",
|
|
1127
|
-
items: [
|
|
1128
|
-
{ icon: "活", name: "活动列表", desc: "查看进行中/未开始的活动" },
|
|
1129
|
-
{ icon: "领", name: "领取活动", desc: "领取指定(或最新)活动奖励" }
|
|
1130
|
-
]
|
|
1131
|
-
}
|
|
1132
|
-
];
|
|
1133
|
-
const handleMenuGroups = [
|
|
1134
|
-
{
|
|
1135
|
-
title: "句柄管理",
|
|
1136
|
-
items: [
|
|
1137
|
-
{ icon: "绑", name: "绑定句柄", desc: "绑定星际争霸2游戏句柄,格式:[区域ID]-S2-[服务器ID]-[档案ID]" },
|
|
1138
|
-
{ icon: "切", name: "切换", desc: "切换正在使用的游戏句柄" },
|
|
1139
|
-
{ icon: "解", name: "解绑句柄", desc: "解除绑定某个游戏句柄" }
|
|
1140
|
-
]
|
|
1141
|
-
},
|
|
1142
|
-
{
|
|
1143
|
-
title: "查询与检测",
|
|
1144
|
-
items: [
|
|
1145
|
-
{ icon: "句", name: "句柄", desc: "查询自己(或他人)已绑定的游戏句柄" },
|
|
1146
|
-
{ icon: "查", name: "查询", desc: "查询某游戏句柄是否已被绑定" },
|
|
1147
|
-
{ icon: "图", name: "地图检测", desc: "查询已配置地图的在线状态与离线统计" },
|
|
1148
|
-
{ icon: "封", name: "封禁记录", desc: "查询自己(或他人)的封禁记录,支持翻页" }
|
|
1149
|
-
]
|
|
1150
|
-
}
|
|
1151
|
-
];
|
|
1152
|
-
ctx.command("ggcevo/咕咕之战", "查看咕咕之战玩法指令菜单").action(async (argv) => {
|
|
1153
|
-
const session = argv.session;
|
|
1154
|
-
const html = buildMenuHtml("咕咕之战", "游戏玩法指令(受宵禁影响的玩法见对应指令说明)", gameMenuGroups);
|
|
1155
|
-
const result = await renderMenuImage(html, session);
|
|
1156
|
-
return typeof result === "string" ? result : (0, import_koishi.h)("quote", { id: session.messageId }, result);
|
|
1157
|
-
});
|
|
1158
|
-
ctx.command("ggcevo/菜单", "查看 GGCEVO 句柄管理指令菜单").alias("ggcevo指令").action(async (argv) => {
|
|
1159
|
-
const session = argv.session;
|
|
1160
|
-
const html = buildMenuHtml("GGCEVO", "句柄管理与查询指令", handleMenuGroups);
|
|
1161
|
-
const result = await renderMenuImage(html, session);
|
|
1162
|
-
return typeof result === "string" ? result : (0, import_koishi.h)("quote", { id: session.messageId }, result);
|
|
1163
|
-
});
|
|
1164
1013
|
ctx.command("ggcevo/兑换 <name:string>").action(async (argv, name2) => {
|
|
1165
1014
|
const session = argv.session;
|
|
1166
1015
|
const adminRegistTip = config.exchangeAdminName ? `
|
|
@@ -2970,6 +2819,166 @@ ${rewardLines.join("\n")}` : "";
|
|
|
2970
2819
|
}
|
|
2971
2820
|
return "已退出封禁记录查询。";
|
|
2972
2821
|
});
|
|
2822
|
+
const menuCategories = [
|
|
2823
|
+
{
|
|
2824
|
+
key: "daily",
|
|
2825
|
+
label: "每日玩法",
|
|
2826
|
+
emoji: "📅",
|
|
2827
|
+
desc: "签到 / 挖矿 / 探索 / 补签",
|
|
2828
|
+
items: [
|
|
2829
|
+
{ key: "sign", icon: "签", label: "签到", desc: "每日签到, 获取金币与咕咕币" },
|
|
2830
|
+
{ key: "mine", icon: "挖", label: "挖矿", desc: "领取挂机挖矿收益(每半小时4金币)" },
|
|
2831
|
+
{ key: "explore", icon: "探", label: "探索", desc: "选择星系进行12小时探索" },
|
|
2832
|
+
{ key: "makeup", icon: "补", label: "补签", desc: "使用补签券补签漏签日期" }
|
|
2833
|
+
]
|
|
2834
|
+
},
|
|
2835
|
+
{
|
|
2836
|
+
key: "bag",
|
|
2837
|
+
label: "物品背包",
|
|
2838
|
+
emoji: "🎒",
|
|
2839
|
+
desc: "背包 / 个人信息 / 使用",
|
|
2840
|
+
items: [
|
|
2841
|
+
{ key: "inv", icon: "包", label: "背包", desc: "查看自己的物品背包" },
|
|
2842
|
+
{ key: "profile", icon: "信", label: "个人信息", desc: "查看签到统计与个人信息" },
|
|
2843
|
+
{ key: "use", icon: "用", label: "使用 <名称>", desc: "使用指定物品(如赎罪券等)" }
|
|
2844
|
+
]
|
|
2845
|
+
},
|
|
2846
|
+
{
|
|
2847
|
+
key: "lottery",
|
|
2848
|
+
label: "抽奖兑换",
|
|
2849
|
+
emoji: "🎲",
|
|
2850
|
+
desc: "抽奖 / 抽奖概率 / 兑换 / 兑换列表",
|
|
2851
|
+
items: [
|
|
2852
|
+
{ key: "lottery", icon: "抽", label: "抽奖", desc: "抽奖, 可加选项 -p 奖池ID -c 次数" },
|
|
2853
|
+
{ key: "odds", icon: "率", label: "抽奖概率", desc: "查看各奖池抽奖概率与保底说明" },
|
|
2854
|
+
{ key: "exchange", icon: "兑", label: "兑换 <名称>", desc: "使用兑换券兑换物品" },
|
|
2855
|
+
{ key: "exchangeList", icon: "表", label: "兑换列表", desc: "查看可兑换物品列表" }
|
|
2856
|
+
]
|
|
2857
|
+
},
|
|
2858
|
+
{
|
|
2859
|
+
key: "activity",
|
|
2860
|
+
label: "活动",
|
|
2861
|
+
emoji: "🎉",
|
|
2862
|
+
desc: "活动列表 / 领取活动",
|
|
2863
|
+
items: [
|
|
2864
|
+
{ key: "actList", icon: "活", label: "活动列表", desc: '查看进行中的活动(参数"全部"查看全部)' },
|
|
2865
|
+
{ key: "actClaim", icon: "领", label: "领取活动", desc: "领取指定(或最新)活动奖励" }
|
|
2866
|
+
]
|
|
2867
|
+
},
|
|
2868
|
+
{
|
|
2869
|
+
key: "handle",
|
|
2870
|
+
label: "句柄绑定",
|
|
2871
|
+
emoji: "🔗",
|
|
2872
|
+
desc: "绑定 / 句柄 / 切换 / 查询 / 解绑 / 迁移",
|
|
2873
|
+
items: [
|
|
2874
|
+
{ key: "bind", icon: "绑", label: "绑定 <句柄>", desc: "绑定星际争霸2游戏句柄" },
|
|
2875
|
+
{ key: "list", icon: "句", label: "句柄", desc: "查询已绑定的游戏句柄" },
|
|
2876
|
+
{ key: "switch", icon: "切", label: "切换", desc: "切换正在使用的游戏句柄" },
|
|
2877
|
+
{ key: "check", icon: "查", label: "查询", desc: "查询某句柄是否已被绑定" },
|
|
2878
|
+
{ key: "unbind", icon: "解", label: "解绑", desc: "解除绑定某个游戏句柄" },
|
|
2879
|
+
{ key: "migrate", icon: "迁", label: "迁移 <QQ>", desc: "将指定QQ号绑定的句柄迁移到当前账号" }
|
|
2880
|
+
]
|
|
2881
|
+
},
|
|
2882
|
+
{
|
|
2883
|
+
key: "query",
|
|
2884
|
+
label: "查询信息",
|
|
2885
|
+
emoji: "📊",
|
|
2886
|
+
desc: "地图检测 / 封禁记录 / 签到奖励",
|
|
2887
|
+
items: [
|
|
2888
|
+
{ key: "map", icon: "图", label: "地图检测", desc: "查询已配置地图的检测状态" },
|
|
2889
|
+
{ key: "ban", icon: "封", label: "封禁记录", desc: "查询句柄的封禁记录" },
|
|
2890
|
+
{ key: "reward", icon: "奖", label: "签到奖励", desc: "查看签到奖励规则说明" }
|
|
2891
|
+
]
|
|
2892
|
+
}
|
|
2893
|
+
];
|
|
2894
|
+
const buildTextMenu = /* @__PURE__ */ __name(() => [
|
|
2895
|
+
"🐤 咕咕之战菜单",
|
|
2896
|
+
"",
|
|
2897
|
+
...menuCategories.map((cat) => `${cat.emoji} ${cat.label}(${cat.desc})
|
|
2898
|
+
${cat.items.map((item) => item.label).join(" / ")}`),
|
|
2899
|
+
"",
|
|
2900
|
+
"提示: 直接输入上方指令名即可使用对应功能。"
|
|
2901
|
+
].join("\n"), "buildTextMenu");
|
|
2902
|
+
const buildMenuHtml = /* @__PURE__ */ __name((title, subtitle) => {
|
|
2903
|
+
const palette = ["#4a7dff", "#9b59b6", "#e67e22", "#27ae60", "#e74c3c", "#16a085", "#f39c12", "#8e44ad", "#2980b9", "#d35400", "#2ecc71", "#c0392b"];
|
|
2904
|
+
const groupHtml = menuCategories.map((group) => {
|
|
2905
|
+
const itemRows = group.items.map((item, i) => {
|
|
2906
|
+
const color = palette[i % palette.length];
|
|
2907
|
+
return `
|
|
2908
|
+
<div class="item">
|
|
2909
|
+
<div class="icon" style="background: ${color}">${item.icon}</div>
|
|
2910
|
+
<div class="info">
|
|
2911
|
+
<div class="name">${item.label}</div>
|
|
2912
|
+
<div class="desc">${item.desc}</div>
|
|
2913
|
+
</div>
|
|
2914
|
+
</div>`;
|
|
2915
|
+
}).join("");
|
|
2916
|
+
return `
|
|
2917
|
+
<div class="group">
|
|
2918
|
+
<div class="group-title">${group.label}</div>
|
|
2919
|
+
<div class="list">${itemRows}</div>
|
|
2920
|
+
</div>`;
|
|
2921
|
+
}).join("");
|
|
2922
|
+
return `<!DOCTYPE html>
|
|
2923
|
+
<html lang="zh">
|
|
2924
|
+
<head>
|
|
2925
|
+
<meta charset="UTF-8">
|
|
2926
|
+
<style>
|
|
2927
|
+
body { margin: 0; padding: 24px 28px; background:
|
|
2928
|
+
repeating-linear-gradient(45deg, rgba(255,255,255,0.02) 0 1px, transparent 1px 2px),
|
|
2929
|
+
linear-gradient(135deg, #1b2a4a 0%, #0f1a33 100%);
|
|
2930
|
+
font-family: "Microsoft YaHei", "PingFang SC", sans-serif; color: #e8eefc; }
|
|
2931
|
+
.header { text-align: center; padding-bottom: 18px; border-bottom: 2px solid rgba(120,160,255,0.35); }
|
|
2932
|
+
.title { font-size: 32px; font-weight: bold; letter-spacing: 6px; color: #8fc0ff; text-shadow: 0 0 18px rgba(90,140,255,0.45); }
|
|
2933
|
+
.subtitle { margin-top: 8px; font-size: 14px; color: #93a6c8; }
|
|
2934
|
+
.group { margin-top: 22px; }
|
|
2935
|
+
.group-title { font-size: 15px; font-weight: bold; color: #ffd98a; letter-spacing: 3px; margin-bottom: 10px; padding-left: 10px; border-left: 3px solid #ffd98a; }
|
|
2936
|
+
.list { display: flex; flex-direction: column; gap: 12px; }
|
|
2937
|
+
.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; }
|
|
2938
|
+
.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; }
|
|
2939
|
+
.info { flex: 1; }
|
|
2940
|
+
.name { font-size: 20px; font-weight: bold; color: #ffffff; }
|
|
2941
|
+
.desc { margin-top: 4px; font-size: 13px; color: #9db1d8; line-height: 1.5; }
|
|
2942
|
+
.footer { margin-top: 24px; text-align: center; font-size: 12px; color: #6b7ea6; }
|
|
2943
|
+
</style>
|
|
2944
|
+
</head>
|
|
2945
|
+
<body>
|
|
2946
|
+
<div class="header">
|
|
2947
|
+
<div class="title">${title}</div>
|
|
2948
|
+
<div class="subtitle">${subtitle}</div>
|
|
2949
|
+
</div>
|
|
2950
|
+
${groupHtml}
|
|
2951
|
+
<div class="footer">输入上方指令名即可使用对应功能</div>
|
|
2952
|
+
</body>
|
|
2953
|
+
</html>`;
|
|
2954
|
+
}, "buildMenuHtml");
|
|
2955
|
+
const renderMenuImage = /* @__PURE__ */ __name(async (html) => {
|
|
2956
|
+
const logger = ctx.logger("ggcevo");
|
|
2957
|
+
if (!ctx.puppeteer) {
|
|
2958
|
+
logger.warn("[menu-render] 未启用 puppeteer 服务");
|
|
2959
|
+
return "⚠️ 未启用 puppeteer 服务,无法渲染图片菜单。请安装并启用 `koishi-plugin-puppeteer` 或 `@shangxueink/puppeteer-without-canvas`。";
|
|
2960
|
+
}
|
|
2961
|
+
const page = await ctx.puppeteer.page();
|
|
2962
|
+
try {
|
|
2963
|
+
await page.setViewport({ width: 900, height: 100, deviceScaleFactor: 2 });
|
|
2964
|
+
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
2965
|
+
const img = await page.screenshot({ type: "jpeg", quality: 85, fullPage: true, encoding: "binary" });
|
|
2966
|
+
logger.info("[menu-render] 菜单图渲染完成, 体积 %s KB", (img.length / 1024).toFixed(1));
|
|
2967
|
+
return import_koishi.h.image(`data:image/jpeg;base64,${img.toString("base64")}`);
|
|
2968
|
+
} catch (e) {
|
|
2969
|
+
logger.warn("[menu-render] 渲染菜单图片失败: %o", e);
|
|
2970
|
+
return "⚠️ 菜单图片渲染失败,请查看日志后重试。";
|
|
2971
|
+
} finally {
|
|
2972
|
+
await page.close();
|
|
2973
|
+
}
|
|
2974
|
+
}, "renderMenuImage");
|
|
2975
|
+
ctx.command("咕咕之战", "查看咕咕之战内容分类菜单").alias("菜单").action(async ({ session }) => {
|
|
2976
|
+
if (config.menuStyle === "image") {
|
|
2977
|
+
const result = await renderMenuImage(buildMenuHtml("咕咕之战", "内容分类菜单 · 输入指令名即可使用"));
|
|
2978
|
+
return typeof result === "string" ? result : (0, import_koishi.h)("quote", { id: session.messageId }, result);
|
|
2979
|
+
}
|
|
2980
|
+
return buildTextMenu();
|
|
2981
|
+
});
|
|
2973
2982
|
}
|
|
2974
2983
|
__name(apply, "apply");
|
|
2975
2984
|
function toBeijingTime(isoString) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-ggcevo",
|
|
3
3
|
"description": "GGCEVO 综合插件:星际争霸2游戏句柄绑定 + 签到/抽奖/兑换/背包/活动系统等虚拟物品玩法",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.3.1",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"typings": "lib/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -24,9 +24,6 @@
|
|
|
24
24
|
"service": {
|
|
25
25
|
"required": [
|
|
26
26
|
"database"
|
|
27
|
-
],
|
|
28
|
-
"optional": [
|
|
29
|
-
"puppeteer"
|
|
30
27
|
]
|
|
31
28
|
}
|
|
32
29
|
},
|
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
|
| 配置项 | 类型 | 默认值 | 说明 |
|
|
@@ -96,14 +102,13 @@ npm install koishi-plugin-ggcevo
|
|
|
96
102
|
| `查询 [handle]` | — | 查询某游戏句柄是否已被绑定 |
|
|
97
103
|
| `解绑 [index]` | 解绑句柄 | 解除绑定某个游戏句柄 |
|
|
98
104
|
| `迁移 <qq>` | — | 将指定QQ号绑定的全部游戏句柄迁移到当前账号(用于从 onebot 机器人切换至 QQ 官方机器人后,将旧 QQ 号下绑定的句柄转移到新的 openid 身份下) |
|
|
99
|
-
| `地图检测` | — | 查询已配置地图(`mapMonitorMapIds`)的检测状态:在线状态、最后状态变更时间、24h/30d离线次数、近期事件。当所有配置地图均离线时,底部额外提示推荐游玩的地图名称(需配置 `mapMonitorRecommendMapName`)。需开启 `mapMonitorEnabled` 且配置地图ID |
|
|
100
|
-
| `菜单` | ggcevo指令 | 渲染 GGCEVO 句柄管理指令菜单图片(绑定/句柄/切换/查询/解绑/地图检测/封禁记录)。需安装启用 puppeteer 服务(`koishi-plugin-puppeteer` 或 `@shangxueink/puppeteer-without-canvas` 任选其一) |
|
|
105
|
+
| `地图检测` | — | — | 查询已配置地图(`mapMonitorMapIds`)的检测状态:在线状态、最后状态变更时间、24h/30d离线次数、近期事件。当所有配置地图均离线时,底部额外提示推荐游玩的地图名称(需配置 `mapMonitorRecommendMapName`)。需开启 `mapMonitorEnabled` 且配置地图ID |
|
|
101
106
|
|
|
102
107
|
### 签到与虚拟物品(ggcevo)
|
|
103
108
|
|
|
104
109
|
| 指令 | 别名 | 权限 | 说明 |
|
|
105
110
|
| --- | --- | --- | --- |
|
|
106
|
-
| `咕咕之战` |
|
|
111
|
+
| `咕咕之战` | 菜单 | — | 生成咕咕之战内容分类菜单(「每日玩法」「物品背包」「抽奖兑换」「活动」「句柄绑定」「查询信息」六类,仅收录普通用户可用指令)。根据配置项 `menuStyle` 选择渲染方式:`text`(默认)输出文字分类菜单,`image` 使用 puppeteer 渲染分类图片菜单(需启用 puppeteer 服务,未启用时自动降级为文字菜单) |
|
|
107
112
|
| `签到` | — | — | 每日签到,获取奖励并发放签到券/补签券等。每月首次签到时,若签到者的游戏句柄匹配管理员福利文档记录,额外发放 50 咕咕币津贴(仅校验句柄) |
|
|
108
113
|
| `挖矿` | — | — | 领取挂机挖矿收益(咕咕之战玩法)。每半小时收益 4 金币,单次存储上限 24 小时,领取后立即进入下一轮挖矿。累计挖矿总时长与累计收益金币数会被记录统计。受宵禁影响:17:00-24:00 禁止领取 |
|
|
109
114
|
| `探索` | — | — | 选择星系进行 12 小时探索,结束后领取收益(咕咕之战玩法)。星系:天枢(成功率100%,成功80金币)、赤潮(成功率60%,成功120金币/失败60金币)、千帆(成功率80%,成功50金币/失败25金币,成功时独立概率获得补签券×1(10%)、咕咕币1-3枚(50%),失败无道具)。金币/道具发放到背包,按星系累计统计探索次数/成功次数/累计金币。受宵禁影响:17:00-24:00 禁止 |
|
|
@@ -174,6 +179,30 @@ npm install koishi-plugin-ggcevo
|
|
|
174
179
|
|
|
175
180
|
## 更新日志
|
|
176
181
|
|
|
182
|
+
### v1.3.1
|
|
183
|
+
|
|
184
|
+
取消 Markdown 按钮菜单,改为可配置的文字/图片菜单。
|
|
185
|
+
|
|
186
|
+
- **取消 Markdown 按钮**:移除 `咕咕之战`(别名 `菜单`)的 QQ 官方机器人 Markdown + 按钮渲染及按钮回调处理,改为输出分类菜单(「每日玩法」「物品背包」「抽奖兑换」「活动」「句柄绑定」「查询信息」六类)
|
|
187
|
+
- **新增配置项 `menuStyle`**(默认 `text`):选择菜单渲染方式
|
|
188
|
+
- `text`:文字分类菜单,无需任何额外服务(默认)
|
|
189
|
+
- `image`:图片菜单,使用 puppeteer 将 HTML 渲染为图片后以 base64 data URL 发送(与 preview-help 插件同方案)
|
|
190
|
+
- **可选依赖 puppeteer 服务**:声明 `koishi-plugin-puppeteer`(^3.9.0)与 `@shangxueink/puppeteer-without-canvas`(^3.0.7)为可选 peer 依赖,任选其一安装启用即可渲染图片菜单;`image` 模式未启用 puppeteer 服务时自动降级为文字菜单并提示
|
|
191
|
+
|
|
192
|
+
### v1.3.0
|
|
193
|
+
|
|
194
|
+
咕咕之战菜单改为 QQ 官方机器人 Markdown 按钮菜单(取代 v1.2.0 移除的图片渲染方案,不再依赖 puppeteer)。
|
|
195
|
+
|
|
196
|
+
- **新增指令 `咕咕之战`**(别名 `菜单`):生成咕咕之战内容分类菜单,分为「每日玩法」「物品背包」「抽奖兑换」「活动」「句柄绑定」「查询信息」六类,仅收录普通用户可用的指令(管理员指令不展示)
|
|
197
|
+
- **QQ 官方机器人按钮交互**:菜单使用 Markdown + 按钮渲染。点击分类按钮进入对应子菜单,点击指令按钮直接执行该指令;`兑换`/`使用`/`绑定`/`迁移` 等需要参数的指令使用输入框按钮,点击后在输入框内填写参数即可执行;子菜单底部提供「返回」按钮回到主菜单
|
|
198
|
+
- **纯文本回退**:非 QQ 平台(或 QQ Markdown 消息发送失败)时自动回退为纯文本分类菜单,保证其它平台可用
|
|
199
|
+
- **前置条件**:按钮回调依赖 QQ 官方机器人的 `INTERACTION_CREATE` 互动事件,需在适配器配置中订阅 `INTERACTIONS` 事件(未订阅时按钮不可用,菜单仍以文本展示)
|
|
200
|
+
|
|
201
|
+
### v1.2.0
|
|
202
|
+
|
|
203
|
+
- **移除指令菜单功能**:删除 `咕咕之战` 与 `菜单`(别名 `ggcevo指令`)指令及其渲染代码(renderMenuImage / buildMenuHtml / 菜单分组数据)
|
|
204
|
+
- **移除 puppeteer 依赖**:删除对 `koishi-plugin-puppeteer` 与 `@shangxueink/puppeteer-without-canvas` 的可选依赖声明、`puppeteer` 服务注入及 `import type` 引用。插件不再依赖任何图片渲染服务
|
|
205
|
+
|
|
177
206
|
### v1.1.20
|
|
178
207
|
|
|
179
208
|
- **修复 QQ 官方机器人无法发送菜单图**:经探测确认 QQ 官方上传接口直接上传 4.4MB PNG 可成功,失败源于适配器在文件超过默认阈值 3MB 时改走分片上传(upload_prepare)通道,而该通道在部分环境不可用且错误被静默吞掉。现于 QQ 官方环境下将上传阈值调大至 10MB,菜单图改走已验证可用的普通上传(file_data)通道
|