koishi-plugin-ggcevo 1.0.0
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 +119 -0
- package/lib/index.js +1981 -0
- package/package.json +20 -0
- package/readme.md +5 -0
package/lib/index.js
ADDED
|
@@ -0,0 +1,1981 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name2 in all)
|
|
8
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
Config: () => Config,
|
|
24
|
+
ExchangeConfig: () => ExchangeConfig,
|
|
25
|
+
ItemConfig: () => ItemConfig,
|
|
26
|
+
LotteryPoolConfig: () => LotteryPoolConfig,
|
|
27
|
+
apply: () => apply,
|
|
28
|
+
inject: () => inject,
|
|
29
|
+
name: () => name
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(src_exports);
|
|
32
|
+
var import_koishi = require("koishi");
|
|
33
|
+
var name = "ggcevo";
|
|
34
|
+
var Config = import_koishi.Schema.object({
|
|
35
|
+
mapMonitorEnabled: import_koishi.Schema.boolean().description("是否启用地图检测定时任务").default(false),
|
|
36
|
+
mapMonitorGroups: import_koishi.Schema.array(import_koishi.Schema.string()).description("地图检测广播的群组ID列表").default([]),
|
|
37
|
+
mapMonitorMapIds: import_koishi.Schema.array(import_koishi.Schema.number()).description("需要检测的地图ID列表").default([])
|
|
38
|
+
});
|
|
39
|
+
var inject = {
|
|
40
|
+
required: ["database"]
|
|
41
|
+
};
|
|
42
|
+
var ItemConfig = {
|
|
43
|
+
1: "金币",
|
|
44
|
+
2: "咕咕币",
|
|
45
|
+
3: "兑换券",
|
|
46
|
+
9: "补签券",
|
|
47
|
+
23: "赎罪券"
|
|
48
|
+
};
|
|
49
|
+
var LotteryPoolConfig = {
|
|
50
|
+
1: "金币池",
|
|
51
|
+
2: "普通池",
|
|
52
|
+
3: "皮肤池",
|
|
53
|
+
4: "宠物池"
|
|
54
|
+
};
|
|
55
|
+
var ExchangeConfig = {
|
|
56
|
+
1: { name: "拾荒者", quality: "t3", type: "皮肤" },
|
|
57
|
+
2: { name: "劳工", quality: "t3", type: "皮肤" },
|
|
58
|
+
3: { name: "老兵", quality: "t2", type: "皮肤" },
|
|
59
|
+
4: { name: "合成人", quality: "t2", type: "皮肤" },
|
|
60
|
+
5: { name: "阿斯塔特", quality: "t1", type: "皮肤" },
|
|
61
|
+
6: { name: "皇家指挥官", quality: "t1", type: "皮肤" },
|
|
62
|
+
7: { name: "个性开场白", quality: "t1", type: "入场特效" },
|
|
63
|
+
8: { name: "史蒂夫", quality: "限定", type: "皮肤" },
|
|
64
|
+
9: { name: "ep4", quality: "t0", type: "物品" },
|
|
65
|
+
10: { name: "小狗", quality: "t3", type: "宠物" },
|
|
66
|
+
11: { name: "小猫", quality: "t3", type: "宠物" },
|
|
67
|
+
12: { name: "小黄鸭", quality: "t3", type: "宠物" },
|
|
68
|
+
13: { name: "萌萌熊", quality: "t2", type: "宠物" },
|
|
69
|
+
14: { name: "荆棘蜥蜴", quality: "t2", type: "宠物" },
|
|
70
|
+
15: { name: "萌宠小狗", quality: "t1", type: "宠物" },
|
|
71
|
+
16: { name: "熔岩虫", quality: "t1", type: "宠物" },
|
|
72
|
+
17: { name: "尸甲虫", quality: "t1", type: "宠物" },
|
|
73
|
+
18: { name: "绿毛虫", quality: "t0", type: "宠物" },
|
|
74
|
+
19: { name: "妙蛙种子", quality: "t0", type: "宠物" },
|
|
75
|
+
20: { name: "皮卡丘", quality: "t0", type: "宠物" },
|
|
76
|
+
21: { name: "哆啦A梦", quality: "t0", type: "宠物" },
|
|
77
|
+
22: { name: "角色冠名权", quality: "t0", type: "角色名称", cost: 10 },
|
|
78
|
+
23: { name: "赎罪券", quality: "t0", type: "道具", cost: 2 }
|
|
79
|
+
};
|
|
80
|
+
function apply(ctx, config) {
|
|
81
|
+
ctx.model.extend("sc2arcade_player", {
|
|
82
|
+
id: "unsigned",
|
|
83
|
+
userId: "string",
|
|
84
|
+
regionId: "unsigned",
|
|
85
|
+
realmId: "unsigned",
|
|
86
|
+
profileId: "unsigned",
|
|
87
|
+
createdAt: "timestamp",
|
|
88
|
+
isActive: "boolean"
|
|
89
|
+
}, {
|
|
90
|
+
autoInc: true,
|
|
91
|
+
primary: "id"
|
|
92
|
+
});
|
|
93
|
+
ctx.model.extend("sc2arcade_map_monitor", {
|
|
94
|
+
mapId: "unsigned",
|
|
95
|
+
lastState: "text",
|
|
96
|
+
lastCheckedAt: "timestamp"
|
|
97
|
+
}, {
|
|
98
|
+
primary: "mapId"
|
|
99
|
+
});
|
|
100
|
+
ctx.model.extend("ggcevo_backpack", {
|
|
101
|
+
user_id: "string",
|
|
102
|
+
item_id: "unsigned",
|
|
103
|
+
count: "unsigned"
|
|
104
|
+
}, {
|
|
105
|
+
primary: ["user_id", "item_id"]
|
|
106
|
+
});
|
|
107
|
+
ctx.model.extend("ggcevo_signin_summary", {
|
|
108
|
+
user_id: "string",
|
|
109
|
+
total_days: "unsigned",
|
|
110
|
+
month_days: "unsigned",
|
|
111
|
+
current_month: "unsigned",
|
|
112
|
+
continuous_days: "unsigned",
|
|
113
|
+
last_signin_date: "timestamp",
|
|
114
|
+
update_time: "timestamp",
|
|
115
|
+
last_allowance_month: "unsigned"
|
|
116
|
+
}, {
|
|
117
|
+
primary: "user_id"
|
|
118
|
+
});
|
|
119
|
+
ctx.model.extend("ggcevo_signin_log", {
|
|
120
|
+
id: "unsigned",
|
|
121
|
+
user_id: "string",
|
|
122
|
+
signin_date: "timestamp",
|
|
123
|
+
signin_type: "unsigned",
|
|
124
|
+
reward_config: "string",
|
|
125
|
+
create_time: "timestamp"
|
|
126
|
+
}, {
|
|
127
|
+
primary: "id",
|
|
128
|
+
autoInc: true
|
|
129
|
+
});
|
|
130
|
+
ctx.model.extend("ggcevo_lottery_log", {
|
|
131
|
+
id: "unsigned",
|
|
132
|
+
user_id: "string",
|
|
133
|
+
lottery_id: "unsigned",
|
|
134
|
+
prize_id: "unsigned",
|
|
135
|
+
cost_type: "unsigned",
|
|
136
|
+
cost_num: "unsigned",
|
|
137
|
+
draw_time: "timestamp"
|
|
138
|
+
}, {
|
|
139
|
+
primary: "id",
|
|
140
|
+
autoInc: true
|
|
141
|
+
});
|
|
142
|
+
ctx.model.extend("ggcevo_lottery_status", {
|
|
143
|
+
user_id: "string",
|
|
144
|
+
lottery_id: "unsigned",
|
|
145
|
+
pity_counter: "unsigned",
|
|
146
|
+
total_draw_count: "unsigned",
|
|
147
|
+
pity_triggered_count: "unsigned",
|
|
148
|
+
rare_hit_count: "unsigned",
|
|
149
|
+
update_time: "timestamp"
|
|
150
|
+
}, {
|
|
151
|
+
primary: ["user_id", "lottery_id"]
|
|
152
|
+
});
|
|
153
|
+
ctx.model.extend("ggcevo_exchange_log", {
|
|
154
|
+
id: "unsigned",
|
|
155
|
+
user_id: "string",
|
|
156
|
+
exchange_id: "unsigned",
|
|
157
|
+
cost_type: "unsigned",
|
|
158
|
+
create_time: "timestamp"
|
|
159
|
+
}, {
|
|
160
|
+
primary: "id",
|
|
161
|
+
autoInc: true
|
|
162
|
+
});
|
|
163
|
+
ctx.model.extend("ggcevo_activity", {
|
|
164
|
+
id: "unsigned",
|
|
165
|
+
name: "string",
|
|
166
|
+
description: "string",
|
|
167
|
+
reward_item: "unsigned",
|
|
168
|
+
reward_amount: "unsigned",
|
|
169
|
+
start_time: "timestamp",
|
|
170
|
+
end_time: "timestamp",
|
|
171
|
+
max_claims: "unsigned",
|
|
172
|
+
required_group: "string",
|
|
173
|
+
created_at: "timestamp"
|
|
174
|
+
}, {
|
|
175
|
+
primary: "id",
|
|
176
|
+
autoInc: true
|
|
177
|
+
});
|
|
178
|
+
ctx.model.extend("ggcevo_activity_claim_log", {
|
|
179
|
+
activity_id: "unsigned",
|
|
180
|
+
user_id: "string",
|
|
181
|
+
claimed_at: "timestamp"
|
|
182
|
+
}, {
|
|
183
|
+
primary: ["activity_id", "user_id"]
|
|
184
|
+
});
|
|
185
|
+
if (config.mapMonitorEnabled && config.mapMonitorGroups.length > 0 && config.mapMonitorMapIds.length > 0) {
|
|
186
|
+
ctx.setInterval(async () => {
|
|
187
|
+
try {
|
|
188
|
+
const response = await ctx.http.get("https://server.dreamprotocol.info:13085/mapmonitor/maps");
|
|
189
|
+
const maps = response.maps || [];
|
|
190
|
+
for (const mapId of config.mapMonitorMapIds) {
|
|
191
|
+
const mapData = maps.find((m) => m.mapId === mapId);
|
|
192
|
+
if (!mapData) continue;
|
|
193
|
+
const currentState = JSON.stringify(mapData);
|
|
194
|
+
const [previousRecord] = await ctx.database.get("sc2arcade_map_monitor", { mapId });
|
|
195
|
+
if (!previousRecord) {
|
|
196
|
+
await ctx.database.create("sc2arcade_map_monitor", {
|
|
197
|
+
mapId,
|
|
198
|
+
lastState: currentState,
|
|
199
|
+
lastCheckedAt: /* @__PURE__ */ new Date()
|
|
200
|
+
});
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const prevData = JSON.parse(previousRecord.lastState);
|
|
204
|
+
if (prevData.isOnline !== mapData.isOnline) {
|
|
205
|
+
const message = formatMapMonitorMessage(mapData, previousRecord);
|
|
206
|
+
for (const groupId of config.mapMonitorGroups) {
|
|
207
|
+
let sent = false;
|
|
208
|
+
for (const bot of ctx.bots) {
|
|
209
|
+
try {
|
|
210
|
+
await bot.sendMessage(groupId, message);
|
|
211
|
+
sent = true;
|
|
212
|
+
break;
|
|
213
|
+
} catch (e) {
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (!sent) {
|
|
217
|
+
console.error(`发送地图检测消息到群组 ${groupId} 失败: 所有 bot 均无法发送`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
await ctx.database.set("sc2arcade_map_monitor", { mapId }, {
|
|
222
|
+
lastState: currentState,
|
|
223
|
+
lastCheckedAt: /* @__PURE__ */ new Date()
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.error("地图检测任务执行失败:", error);
|
|
228
|
+
}
|
|
229
|
+
}, 6e4);
|
|
230
|
+
}
|
|
231
|
+
function getRegionName(regionId) {
|
|
232
|
+
const regionMap = {
|
|
233
|
+
1: "[US]",
|
|
234
|
+
2: "[EU]",
|
|
235
|
+
3: "[KR]",
|
|
236
|
+
5: "[CN]"
|
|
237
|
+
};
|
|
238
|
+
return regionMap[regionId] || `[${regionId}]`;
|
|
239
|
+
}
|
|
240
|
+
__name(getRegionName, "getRegionName");
|
|
241
|
+
function formatHandle(handle, isActive = false) {
|
|
242
|
+
const region = getRegionName(handle.regionId);
|
|
243
|
+
const activeMark = isActive ? " (当前使用)" : "";
|
|
244
|
+
return `${region} ${handle.regionId}-S2-${handle.realmId}-${handle.profileId}${activeMark}`;
|
|
245
|
+
}
|
|
246
|
+
__name(formatHandle, "formatHandle");
|
|
247
|
+
const getHandle = /* @__PURE__ */ __name(async (session) => {
|
|
248
|
+
const [profile] = await ctx.database.get("sc2arcade_player", { userId: session.userId, isActive: true });
|
|
249
|
+
if (!profile) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
const { regionId, realmId, profileId } = profile;
|
|
253
|
+
return `${regionId}-S2-${realmId}-${profileId}`;
|
|
254
|
+
}, "getHandle");
|
|
255
|
+
ctx.command("sc2arcade/绑定 [handle]", "绑定星际争霸2游戏句柄").alias("绑定句柄").usage("游戏句柄格式为: [区域ID]-S2-[服务器ID]-[档案ID]").action(async (argv, handle) => {
|
|
256
|
+
const session = argv.session;
|
|
257
|
+
if (!handle) {
|
|
258
|
+
await session.send(`<quote id="${session.messageId}"/>请在30秒内输入游戏句柄:
|
|
259
|
+
(游戏句柄格式为: [区域ID]-S2-[服务器ID]-[档案ID])
|
|
260
|
+
例如:5-S2-1-1234567`);
|
|
261
|
+
handle = await session.prompt(3e4);
|
|
262
|
+
if (!handle) return `<quote id="${session.messageId}"/>已取消操作, 请重新输入。`;
|
|
263
|
+
}
|
|
264
|
+
const handleRegex = /^([1235])-s2-([12])-(\d+)$/i;
|
|
265
|
+
if (!handleRegex.test(handle)) {
|
|
266
|
+
return `<quote id="${session.messageId}"/>❌ 游戏句柄格式错误, 请重新输入。
|
|
267
|
+
(游戏句柄格式为: [区域ID]-S2-[服务器ID]-[档案ID])
|
|
268
|
+
例如:5-S2-1-1234567`;
|
|
269
|
+
}
|
|
270
|
+
const standardizedHandle = handle.replace(/-s2-/i, "-S2-");
|
|
271
|
+
const [, regionId, realmId, profileId] = standardizedHandle.match(handleRegex).map(Number);
|
|
272
|
+
const existingHandle = await ctx.database.get("sc2arcade_player", {
|
|
273
|
+
regionId,
|
|
274
|
+
realmId,
|
|
275
|
+
profileId
|
|
276
|
+
});
|
|
277
|
+
if (existingHandle.length > 0) {
|
|
278
|
+
return `<quote id="${session.messageId}"/>❌ 绑定失败, 该游戏句柄已被其他用户绑定。`;
|
|
279
|
+
}
|
|
280
|
+
const userHandles = await ctx.database.get("sc2arcade_player", { userId: session.userId });
|
|
281
|
+
const alreadyBound = userHandles.some(
|
|
282
|
+
(h2) => h2.regionId === regionId && h2.realmId === realmId && h2.profileId === profileId
|
|
283
|
+
);
|
|
284
|
+
if (alreadyBound) {
|
|
285
|
+
return `<quote id="${session.messageId}"/>❌ 您已绑定过该游戏句柄。`;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
await makeHttpRequest(
|
|
289
|
+
ctx,
|
|
290
|
+
`https://api.sc2arcade.com/profiles/${regionId}/${realmId}/${profileId}`
|
|
291
|
+
);
|
|
292
|
+
const isFirstHandle = userHandles.length === 0;
|
|
293
|
+
await ctx.database.create("sc2arcade_player", {
|
|
294
|
+
userId: session.userId,
|
|
295
|
+
regionId,
|
|
296
|
+
realmId,
|
|
297
|
+
profileId,
|
|
298
|
+
isActive: isFirstHandle,
|
|
299
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
300
|
+
});
|
|
301
|
+
return `<quote id="${session.messageId}"/>✅ 您已成功绑定游戏句柄${isFirstHandle ? "并设为当前使用" : ""}并已通过验证。`;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
if (error.response && error.response.status === 404) {
|
|
304
|
+
return `<quote id="${session.messageId}"/>❌ 绑定失败, 您尝试绑定的游戏句柄不存在。`;
|
|
305
|
+
}
|
|
306
|
+
console.error("查询或绑定失败:", error);
|
|
307
|
+
return "⚠️ 服务器繁忙, 请稍后尝试。";
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
ctx.command("sc2arcade/句柄 [user]", "查询已经绑定的星际争霸2游戏句柄").usage("user 参数为选填项").example("/句柄, 查询自己绑定的游戏句柄\n /句柄 @用户, 查询其他用户绑定的游戏句柄").action(async (argv, user) => {
|
|
311
|
+
const session = argv.session;
|
|
312
|
+
try {
|
|
313
|
+
if (!user) {
|
|
314
|
+
const handles = await ctx.database.get("sc2arcade_player", { userId: session.userId });
|
|
315
|
+
if (!handles || handles.length === 0) {
|
|
316
|
+
return `<quote id="${session.messageId}"/>您暂未绑定游戏句柄。`;
|
|
317
|
+
}
|
|
318
|
+
const message = handles.map(
|
|
319
|
+
(h2, index) => `${index + 1}. ${formatHandle(h2, h2.isActive)}`
|
|
320
|
+
).join("\n");
|
|
321
|
+
return `<quote id="${session.messageId}"/>您绑定的游戏句柄:
|
|
322
|
+
${message}`;
|
|
323
|
+
} else {
|
|
324
|
+
const parsedUser = import_koishi.h.parse(user)[0];
|
|
325
|
+
if (!parsedUser || parsedUser.type !== "at" || !parsedUser.attrs.id) {
|
|
326
|
+
return `<quote id="${session.messageId}"/>❌ 参数错误, 请输入"句柄 @用户"查询其他用户绑定的游戏句柄。`;
|
|
327
|
+
}
|
|
328
|
+
const targetUserId = parsedUser.attrs.id;
|
|
329
|
+
const handles = await ctx.database.get("sc2arcade_player", { userId: targetUserId });
|
|
330
|
+
if (!handles || handles.length === 0) {
|
|
331
|
+
return `<quote id="${session.messageId}"/>对方暂未绑定游戏句柄。`;
|
|
332
|
+
}
|
|
333
|
+
const message = handles.map(
|
|
334
|
+
(h2, index) => `${index + 1}. ${formatHandle(h2, h2.isActive)}`
|
|
335
|
+
).join("\n");
|
|
336
|
+
return `<quote id="${session.messageId}"/>对方绑定的游戏句柄:
|
|
337
|
+
${message}`;
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.error("查询句柄信息时发生错误:", error);
|
|
341
|
+
return "⚠️ 服务器繁忙, 请稍后尝试。";
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
ctx.command("sc2arcade/切换 [index]", "切换正在使用的游戏句柄").action(async (argv, indexParam) => {
|
|
345
|
+
const session = argv.session;
|
|
346
|
+
try {
|
|
347
|
+
const handles = await ctx.database.get("sc2arcade_player", { userId: session.userId });
|
|
348
|
+
if (!handles || handles.length === 0) {
|
|
349
|
+
return `<quote id="${session.messageId}"/>您暂未绑定游戏句柄。`;
|
|
350
|
+
}
|
|
351
|
+
let index = null;
|
|
352
|
+
if (!indexParam) {
|
|
353
|
+
const message = handles.map(
|
|
354
|
+
(h2, i) => `${i + 1}. ${formatHandle(h2, h2.isActive)}`
|
|
355
|
+
).join("\n");
|
|
356
|
+
await session.send(`<quote id="${session.messageId}"/>请选择要切换的句柄:
|
|
357
|
+
${message}
|
|
358
|
+
|
|
359
|
+
回复序号进行切换`);
|
|
360
|
+
const choice = await session.prompt(3e4);
|
|
361
|
+
if (!choice) return `<quote id="${session.messageId}"/>已取消操作。`;
|
|
362
|
+
index = parseInt(choice);
|
|
363
|
+
} else {
|
|
364
|
+
index = parseInt(indexParam);
|
|
365
|
+
}
|
|
366
|
+
if (isNaN(index) || index < 1 || index > handles.length) {
|
|
367
|
+
return `<quote id="${session.messageId}"/>❌ 序号无效,请输入1-${handles.length}之间的数字。`;
|
|
368
|
+
}
|
|
369
|
+
const selectedHandle = handles[index - 1];
|
|
370
|
+
await Promise.all(handles.map(
|
|
371
|
+
(handle) => ctx.database.set("sc2arcade_player", { id: handle.id }, { isActive: handle.id === selectedHandle.id })
|
|
372
|
+
));
|
|
373
|
+
return `<quote id="${session.messageId}"/>✅ 已切换到句柄:${formatHandle(selectedHandle)}`;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
console.error("切换句柄时发生错误:", error);
|
|
376
|
+
return "⚠️ 切换失败,请稍后尝试。";
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
ctx.command("sc2arcade/查询 [handle]", "查询星际争霸2游戏句柄是否被绑定").action(async (argv, handle) => {
|
|
380
|
+
const session = argv.session;
|
|
381
|
+
try {
|
|
382
|
+
if (!handle) {
|
|
383
|
+
await session.send(`<quote id="${session.messageId}"/>请在30秒内输入游戏句柄:
|
|
384
|
+
(游戏句柄格式为: [区域ID]-S2-[服务器ID]-[档案ID])`);
|
|
385
|
+
handle = await session.prompt(3e4);
|
|
386
|
+
if (!handle) return `<quote id="${session.messageId}"/>已取消操作, 请重新输入。`;
|
|
387
|
+
}
|
|
388
|
+
const handleRegex = /^([1235])-S2-([12])-(\d+)$/;
|
|
389
|
+
if (!handleRegex.test(handle)) {
|
|
390
|
+
return `<quote id="${session.messageId}"/>❌ 游戏句柄格式错误, 请重新输入。
|
|
391
|
+
(游戏句柄格式为: [区域ID]-S2-[服务器ID]-[档案ID])`;
|
|
392
|
+
}
|
|
393
|
+
const [, regionId, realmId, profileId] = handle.match(handleRegex).map(Number);
|
|
394
|
+
const existingHandle = await ctx.database.get("sc2arcade_player", {
|
|
395
|
+
regionId,
|
|
396
|
+
realmId,
|
|
397
|
+
profileId
|
|
398
|
+
});
|
|
399
|
+
if (existingHandle.length > 0) {
|
|
400
|
+
return `<quote id="${session.messageId}"/>该游戏句柄已被 ${existingHandle[0].userId} 绑定。`;
|
|
401
|
+
} else {
|
|
402
|
+
return `<quote id="${session.messageId}"/>该游戏句柄暂未被其他用户绑定。`;
|
|
403
|
+
}
|
|
404
|
+
} catch (error) {
|
|
405
|
+
console.error("查询句柄信息时发生错误:", error);
|
|
406
|
+
return "⚠️ 服务器繁忙, 请稍后尝试。";
|
|
407
|
+
}
|
|
408
|
+
});
|
|
409
|
+
ctx.command("sc2arcade/解绑 [index]", "解除绑定星际争霸2游戏句柄").alias("解绑句柄").action(async (argv, indexParam) => {
|
|
410
|
+
const session = argv.session;
|
|
411
|
+
try {
|
|
412
|
+
const handles = await ctx.database.get("sc2arcade_player", { userId: session.userId });
|
|
413
|
+
if (handles.length === 0) {
|
|
414
|
+
return `<quote id="${session.messageId}"/>您暂未绑定游戏句柄。`;
|
|
415
|
+
}
|
|
416
|
+
let index = null;
|
|
417
|
+
if (!indexParam) {
|
|
418
|
+
const message = handles.map(
|
|
419
|
+
(h2, i) => `${i + 1}. ${formatHandle(h2, h2.isActive)}`
|
|
420
|
+
).join("\n");
|
|
421
|
+
await session.send(`<quote id="${session.messageId}"/>请选择要解绑的句柄:
|
|
422
|
+
${message}
|
|
423
|
+
|
|
424
|
+
回复序号进行解绑`);
|
|
425
|
+
const choice = await session.prompt(3e4);
|
|
426
|
+
if (!choice) return `<quote id="${session.messageId}"/>已取消操作。`;
|
|
427
|
+
index = parseInt(choice);
|
|
428
|
+
} else {
|
|
429
|
+
index = parseInt(indexParam);
|
|
430
|
+
}
|
|
431
|
+
if (isNaN(index) || index < 1 || index > handles.length) {
|
|
432
|
+
return `<quote id="${session.messageId}"/>❌ 序号无效,请输入1-${handles.length}之间的数字。`;
|
|
433
|
+
}
|
|
434
|
+
const handleToRemove = handles[index - 1];
|
|
435
|
+
const wasActive = handleToRemove.isActive;
|
|
436
|
+
await ctx.database.remove("sc2arcade_player", { id: handleToRemove.id });
|
|
437
|
+
if (wasActive && handles.length > 1) {
|
|
438
|
+
const nextHandle = handles.find((h2) => h2.id !== handleToRemove.id);
|
|
439
|
+
if (nextHandle) {
|
|
440
|
+
await ctx.database.set("sc2arcade_player", { id: nextHandle.id }, { isActive: true });
|
|
441
|
+
return `<quote id="${session.messageId}"/>✅ 已解绑句柄,并自动切换到:${formatHandle(nextHandle)}`;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return `<quote id="${session.messageId}"/>✅ 已成功解绑句柄。`;
|
|
445
|
+
} catch (error) {
|
|
446
|
+
console.error("解绑失败:", error);
|
|
447
|
+
return "⚠️ 服务器繁忙, 请稍后尝试。";
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
ctx.command("ggcevo/签到").action(async (argv) => {
|
|
451
|
+
const session = argv.session;
|
|
452
|
+
const handle = await getHandle(session);
|
|
453
|
+
if (!handle) {
|
|
454
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
455
|
+
}
|
|
456
|
+
const now = /* @__PURE__ */ new Date();
|
|
457
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
458
|
+
const [existingLog] = await ctx.database.get("ggcevo_signin_log", {
|
|
459
|
+
user_id: handle,
|
|
460
|
+
signin_type: 0,
|
|
461
|
+
signin_date: { $gte: today }
|
|
462
|
+
});
|
|
463
|
+
if (existingLog) {
|
|
464
|
+
return "📅 今日已签到,明天再来吧!";
|
|
465
|
+
}
|
|
466
|
+
const goldReward = Math.floor(Math.random() * 11) + 10;
|
|
467
|
+
let gugubReward = 3;
|
|
468
|
+
let extraGugubReward = 0;
|
|
469
|
+
let monthlyAllowance = 0;
|
|
470
|
+
const [summary] = await ctx.database.get("ggcevo_signin_summary", { user_id: handle });
|
|
471
|
+
const currentMonth = now.getFullYear() * 100 + (now.getMonth() + 1);
|
|
472
|
+
let newTotalDays = 1;
|
|
473
|
+
let newMonthDays = 1;
|
|
474
|
+
let newContinuousDays = 1;
|
|
475
|
+
let lastAllowanceMonth = 0;
|
|
476
|
+
if (summary) {
|
|
477
|
+
newTotalDays = summary.total_days + 1;
|
|
478
|
+
if (summary.current_month === currentMonth) {
|
|
479
|
+
newMonthDays = summary.month_days + 1;
|
|
480
|
+
} else {
|
|
481
|
+
newMonthDays = 1;
|
|
482
|
+
}
|
|
483
|
+
const lastSignin = new Date(summary.last_signin_date);
|
|
484
|
+
const lastSigninDate = new Date(lastSignin.getFullYear(), lastSignin.getMonth(), lastSignin.getDate());
|
|
485
|
+
const dayDiff = Math.floor((today.getTime() - lastSigninDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
486
|
+
if (dayDiff === 1) {
|
|
487
|
+
newContinuousDays = summary.continuous_days + 1;
|
|
488
|
+
} else {
|
|
489
|
+
newContinuousDays = 1;
|
|
490
|
+
}
|
|
491
|
+
lastAllowanceMonth = summary.last_allowance_month || 0;
|
|
492
|
+
}
|
|
493
|
+
const memberInfo = session.event?.member?.roles;
|
|
494
|
+
const isAdmin = !memberInfo?.some((role) => role.name === "member" || role.id === "member");
|
|
495
|
+
if (isAdmin && lastAllowanceMonth !== currentMonth) {
|
|
496
|
+
monthlyAllowance = 50;
|
|
497
|
+
gugubReward += monthlyAllowance;
|
|
498
|
+
lastAllowanceMonth = currentMonth;
|
|
499
|
+
}
|
|
500
|
+
const monthRewardConfig = {
|
|
501
|
+
7: 1,
|
|
502
|
+
14: 2,
|
|
503
|
+
21: 3,
|
|
504
|
+
28: 4
|
|
505
|
+
};
|
|
506
|
+
if (monthRewardConfig[newMonthDays]) {
|
|
507
|
+
extraGugubReward = monthRewardConfig[newMonthDays];
|
|
508
|
+
gugubReward += extraGugubReward;
|
|
509
|
+
}
|
|
510
|
+
await ctx.database.upsert("ggcevo_signin_summary", [{
|
|
511
|
+
user_id: handle,
|
|
512
|
+
total_days: newTotalDays,
|
|
513
|
+
month_days: newMonthDays,
|
|
514
|
+
current_month: currentMonth,
|
|
515
|
+
continuous_days: newContinuousDays,
|
|
516
|
+
last_signin_date: now,
|
|
517
|
+
update_time: now,
|
|
518
|
+
last_allowance_month: lastAllowanceMonth
|
|
519
|
+
}]);
|
|
520
|
+
await ctx.database.create("ggcevo_signin_log", {
|
|
521
|
+
user_id: handle,
|
|
522
|
+
signin_date: now,
|
|
523
|
+
signin_type: 0,
|
|
524
|
+
reward_config: "daily",
|
|
525
|
+
create_time: now
|
|
526
|
+
});
|
|
527
|
+
const updateBackpackItem = /* @__PURE__ */ __name(async (itemId, count) => {
|
|
528
|
+
const [existing] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: itemId });
|
|
529
|
+
const newCount = (existing?.count || 0) + count;
|
|
530
|
+
if (existing) {
|
|
531
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
532
|
+
user_id: handle,
|
|
533
|
+
item_id: itemId,
|
|
534
|
+
count: newCount
|
|
535
|
+
}]);
|
|
536
|
+
} else {
|
|
537
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
538
|
+
user_id: handle,
|
|
539
|
+
item_id: itemId,
|
|
540
|
+
count: newCount
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
}, "updateBackpackItem");
|
|
544
|
+
await updateBackpackItem(1, goldReward);
|
|
545
|
+
await updateBackpackItem(2, gugubReward);
|
|
546
|
+
let message = `🎁 签到成功!
|
|
547
|
+
获得 ${goldReward} 金币
|
|
548
|
+
获得 3 咕咕币
|
|
549
|
+
累计签到 ${newTotalDays} 天
|
|
550
|
+
连续签到 ${newContinuousDays} 天`;
|
|
551
|
+
if (extraGugubReward > 0) {
|
|
552
|
+
message += `
|
|
553
|
+
⭐ 本月第${newMonthDays}次签到额外奖励:${extraGugubReward} 咕咕币`;
|
|
554
|
+
}
|
|
555
|
+
if (monthlyAllowance > 0) {
|
|
556
|
+
message += `
|
|
557
|
+
💰 每月津贴:+${monthlyAllowance} 咕咕币`;
|
|
558
|
+
}
|
|
559
|
+
return message;
|
|
560
|
+
});
|
|
561
|
+
ctx.command("ggcevo/兑换 <name:string>").action(async (argv, name2) => {
|
|
562
|
+
const session = argv.session;
|
|
563
|
+
const handle = await getHandle(session);
|
|
564
|
+
if (!handle) {
|
|
565
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
566
|
+
}
|
|
567
|
+
const now = /* @__PURE__ */ new Date();
|
|
568
|
+
const dayOfWeek = now.getDay();
|
|
569
|
+
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
|
570
|
+
return "❌ 兑换功能仅限工作日使用(周一至周五),请在工作日再来兑换!";
|
|
571
|
+
}
|
|
572
|
+
if (!name2) {
|
|
573
|
+
return "❌ 请输入兑换物品的名称!\n使用 `兑换列表` 查看可兑换物品。";
|
|
574
|
+
}
|
|
575
|
+
const matchedItems = Object.entries(ExchangeConfig).filter(
|
|
576
|
+
([_, item]) => item.name.includes(name2)
|
|
577
|
+
);
|
|
578
|
+
if (matchedItems.length === 0) {
|
|
579
|
+
return `❌ 不存在名为"${name2}"的兑换物品!
|
|
580
|
+
使用 \`兑换列表\` 查看可兑换物品。`;
|
|
581
|
+
}
|
|
582
|
+
if (matchedItems.length > 1) {
|
|
583
|
+
const itemNames = matchedItems.map(([_, item]) => item.name).join("、");
|
|
584
|
+
return `❌ 名称"${name2}"匹配多个物品:${itemNames}
|
|
585
|
+
请输入更完整的物品名称。`;
|
|
586
|
+
}
|
|
587
|
+
const [id, exchangeItem] = matchedItems[0];
|
|
588
|
+
if (exchangeItem.quality === "限定") {
|
|
589
|
+
return `❌ ${exchangeItem.name} 为限定物品,不可兑换!`;
|
|
590
|
+
}
|
|
591
|
+
const costMap = {
|
|
592
|
+
"t3": 3,
|
|
593
|
+
"t2": 4,
|
|
594
|
+
"t1": 5,
|
|
595
|
+
"t0": 6
|
|
596
|
+
};
|
|
597
|
+
const costCount = exchangeItem.cost ?? costMap[exchangeItem.quality];
|
|
598
|
+
const [couponItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 3 });
|
|
599
|
+
const couponCount = couponItem?.count || 0;
|
|
600
|
+
if (couponCount < costCount) {
|
|
601
|
+
return `❌ 兑换券不足!需要 ${costCount} 张兑换券,当前拥有 ${couponCount} 张。`;
|
|
602
|
+
}
|
|
603
|
+
if (exchangeItem.type === "道具") {
|
|
604
|
+
const itemId = Number(id);
|
|
605
|
+
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
606
|
+
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
|
|
607
|
+
const [thisMonthExchange] = await ctx.database.get("ggcevo_exchange_log", {
|
|
608
|
+
user_id: handle,
|
|
609
|
+
exchange_id: itemId,
|
|
610
|
+
cost_type: 3,
|
|
611
|
+
create_time: { $gte: startOfMonth, $lte: endOfMonth }
|
|
612
|
+
});
|
|
613
|
+
if (thisMonthExchange) {
|
|
614
|
+
return `❌ 本月已兑换过 ${exchangeItem.name},每月仅限兑换1个!`;
|
|
615
|
+
}
|
|
616
|
+
const [backpackItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: itemId });
|
|
617
|
+
if (backpackItem && backpackItem.count >= 1) {
|
|
618
|
+
return `❌ 你已经拥有 ${exchangeItem.name},使用后才可以再次兑换!`;
|
|
619
|
+
}
|
|
620
|
+
} else if (exchangeItem.type !== "角色名称") {
|
|
621
|
+
const [alreadyOwned] = await ctx.database.get("ggcevo_exchange_log", {
|
|
622
|
+
user_id: handle,
|
|
623
|
+
exchange_id: Number(id)
|
|
624
|
+
});
|
|
625
|
+
if (alreadyOwned) {
|
|
626
|
+
return `❌ 你已经拥有 ${exchangeItem.name},不可重复兑换!`;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
const newCouponCount = couponCount - costCount;
|
|
630
|
+
if (couponItem) {
|
|
631
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
632
|
+
user_id: handle,
|
|
633
|
+
item_id: 3,
|
|
634
|
+
count: newCouponCount
|
|
635
|
+
}]);
|
|
636
|
+
}
|
|
637
|
+
if (exchangeItem.type === "道具") {
|
|
638
|
+
const itemId = Number(id);
|
|
639
|
+
const [existingBackpack] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: itemId });
|
|
640
|
+
if (existingBackpack) {
|
|
641
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
642
|
+
user_id: handle,
|
|
643
|
+
item_id: itemId,
|
|
644
|
+
count: 1
|
|
645
|
+
}]);
|
|
646
|
+
} else {
|
|
647
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
648
|
+
user_id: handle,
|
|
649
|
+
item_id: itemId,
|
|
650
|
+
count: 1
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
await ctx.database.create("ggcevo_exchange_log", {
|
|
654
|
+
user_id: handle,
|
|
655
|
+
exchange_id: itemId,
|
|
656
|
+
cost_type: 3,
|
|
657
|
+
create_time: now
|
|
658
|
+
});
|
|
659
|
+
return `🎁 兑换成功!
|
|
660
|
+
消耗 ${costCount} 张兑换券
|
|
661
|
+
获得 ${exchangeItem.name}
|
|
662
|
+
💡 使用 \`使用 赎罪券\` 来消耗此道具`;
|
|
663
|
+
}
|
|
664
|
+
await ctx.database.create("ggcevo_exchange_log", {
|
|
665
|
+
user_id: handle,
|
|
666
|
+
exchange_id: Number(id),
|
|
667
|
+
cost_type: 1,
|
|
668
|
+
create_time: now
|
|
669
|
+
});
|
|
670
|
+
return `🎁 兑换成功!
|
|
671
|
+
消耗 ${costCount} 张兑换券
|
|
672
|
+
获得 ${exchangeItem.name}(${exchangeItem.quality} - ${exchangeItem.type})`;
|
|
673
|
+
});
|
|
674
|
+
ctx.command("ggcevo/抽奖").option("poolId", "-p <poolId:number> 抽奖池ID").option("count", "-c <count:number> 抽奖次数").action(async (argv) => {
|
|
675
|
+
const session = argv.session;
|
|
676
|
+
const { poolId = 2, count } = argv.options;
|
|
677
|
+
const handle = await getHandle(session);
|
|
678
|
+
if (!handle) {
|
|
679
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
680
|
+
}
|
|
681
|
+
const poolName = LotteryPoolConfig[poolId] || "未知池";
|
|
682
|
+
if (!LotteryPoolConfig[poolId]) {
|
|
683
|
+
return `❌ 不存在ID为 ${poolId} 的奖池!`;
|
|
684
|
+
}
|
|
685
|
+
const isGoldPool = poolId === 1;
|
|
686
|
+
const isSkinPool = poolId === 3;
|
|
687
|
+
const isPetPool = poolId === 4;
|
|
688
|
+
let costItemId;
|
|
689
|
+
let costPerDraw;
|
|
690
|
+
if (isGoldPool) {
|
|
691
|
+
costItemId = 1;
|
|
692
|
+
costPerDraw = 100;
|
|
693
|
+
} else if (isSkinPool || isPetPool) {
|
|
694
|
+
costItemId = 3;
|
|
695
|
+
costPerDraw = 3;
|
|
696
|
+
} else {
|
|
697
|
+
costItemId = 2;
|
|
698
|
+
costPerDraw = 1;
|
|
699
|
+
}
|
|
700
|
+
const [costItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: costItemId });
|
|
701
|
+
const costItemCount = costItem?.count || 0;
|
|
702
|
+
let drawCount;
|
|
703
|
+
const maxDrawCount = Math.floor(costItemCount / costPerDraw);
|
|
704
|
+
if (count !== void 0) {
|
|
705
|
+
if (count <= 0) {
|
|
706
|
+
return "❌ 抽奖次数必须大于0!";
|
|
707
|
+
}
|
|
708
|
+
if (isGoldPool && count % 10 !== 0) {
|
|
709
|
+
return "❌ 金币池仅支持10连抽,抽奖次数必须为10的倍数(如10、20、30...)!";
|
|
710
|
+
}
|
|
711
|
+
if (count > maxDrawCount) {
|
|
712
|
+
let costItemName2;
|
|
713
|
+
if (isGoldPool) {
|
|
714
|
+
costItemName2 = "金币";
|
|
715
|
+
} else if (isSkinPool || isPetPool) {
|
|
716
|
+
costItemName2 = "兑换券";
|
|
717
|
+
} else {
|
|
718
|
+
costItemName2 = "咕咕币";
|
|
719
|
+
}
|
|
720
|
+
return `❌ ${costItemName2}不足,当前拥有 ${costItemCount} ${costItemName2},需要 ${count * costPerDraw} ${costItemName2}!`;
|
|
721
|
+
}
|
|
722
|
+
drawCount = count;
|
|
723
|
+
} else {
|
|
724
|
+
if (maxDrawCount <= 0 || isGoldPool && maxDrawCount < 10) {
|
|
725
|
+
let costItemName2;
|
|
726
|
+
let required;
|
|
727
|
+
if (isGoldPool) {
|
|
728
|
+
costItemName2 = "金币";
|
|
729
|
+
required = 10 * costPerDraw;
|
|
730
|
+
} else if (isSkinPool || isPetPool) {
|
|
731
|
+
costItemName2 = "兑换券";
|
|
732
|
+
required = 1 * costPerDraw;
|
|
733
|
+
} else {
|
|
734
|
+
costItemName2 = "咕咕币";
|
|
735
|
+
required = 1 * costPerDraw;
|
|
736
|
+
}
|
|
737
|
+
return `❌ ${costItemName2}不足,当前拥有 ${costItemCount} ${costItemName2},至少需要 ${required} ${costItemName2}!`;
|
|
738
|
+
}
|
|
739
|
+
const isNormalPool = !isGoldPool && !isSkinPool && !isPetPool;
|
|
740
|
+
drawCount = isGoldPool ? 10 : isNormalPool ? maxDrawCount : 1;
|
|
741
|
+
}
|
|
742
|
+
const now = /* @__PURE__ */ new Date();
|
|
743
|
+
const [lotteryStatus] = await ctx.database.get("ggcevo_lottery_status", { user_id: handle, lottery_id: poolId });
|
|
744
|
+
let pityCounter = lotteryStatus?.pity_counter || 0;
|
|
745
|
+
let rareHitCount = lotteryStatus?.rare_hit_count || 0;
|
|
746
|
+
let pityTriggered = false;
|
|
747
|
+
const ownedItems = /* @__PURE__ */ new Set();
|
|
748
|
+
const exchangeLogs = await ctx.database.get("ggcevo_exchange_log", { user_id: handle });
|
|
749
|
+
for (const log of exchangeLogs) {
|
|
750
|
+
ownedItems.add(log.exchange_id);
|
|
751
|
+
}
|
|
752
|
+
if (isSkinPool) {
|
|
753
|
+
const allSkinIds = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "皮肤" && item.quality !== "限定").map(([id]) => parseInt(id));
|
|
754
|
+
if (allSkinIds.length > 0 && allSkinIds.every((id) => ownedItems.has(id))) {
|
|
755
|
+
return "❌ 你已经拥有全部可抽奖的皮肤,无法继续抽奖!";
|
|
756
|
+
}
|
|
757
|
+
} else if (isPetPool) {
|
|
758
|
+
const allPetIds = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "宠物").map(([id]) => parseInt(id));
|
|
759
|
+
if (allPetIds.length > 0 && allPetIds.every((id) => ownedItems.has(id))) {
|
|
760
|
+
return "❌ 你已经拥有全部可抽奖的宠物,无法继续抽奖!";
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
const rewards = [];
|
|
764
|
+
let totalGold = 0;
|
|
765
|
+
let totalCoupon = 0;
|
|
766
|
+
let totalMakeupCoupon = 0;
|
|
767
|
+
let totalGugub = 0;
|
|
768
|
+
let nothingCount = 0;
|
|
769
|
+
for (let i = 0; i < drawCount; i++) {
|
|
770
|
+
const isNormalPool = !isGoldPool && !isSkinPool;
|
|
771
|
+
if (isNormalPool) {
|
|
772
|
+
pityCounter++;
|
|
773
|
+
}
|
|
774
|
+
let gotSSR = false;
|
|
775
|
+
if (isGoldPool) {
|
|
776
|
+
const rand = Math.random() * 100;
|
|
777
|
+
if (rand < 20) {
|
|
778
|
+
nothingCount++;
|
|
779
|
+
} else if (rand < 70) {
|
|
780
|
+
rewards.push({ itemId: 1, count: 80 });
|
|
781
|
+
totalGold += 80;
|
|
782
|
+
} else if (rand < 85) {
|
|
783
|
+
rewards.push({ itemId: 1, count: 150 });
|
|
784
|
+
totalGold += 150;
|
|
785
|
+
} else if (rand < 95) {
|
|
786
|
+
rewards.push({ itemId: 2, count: 1 });
|
|
787
|
+
totalGugub += 1;
|
|
788
|
+
} else {
|
|
789
|
+
rewards.push({ itemId: 9, count: 1 });
|
|
790
|
+
totalMakeupCoupon += 1;
|
|
791
|
+
gotSSR = true;
|
|
792
|
+
rareHitCount++;
|
|
793
|
+
}
|
|
794
|
+
} else if (isSkinPool) {
|
|
795
|
+
const skinItems = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "皮肤" && item.quality !== "限定");
|
|
796
|
+
let t3Skins = skinItems.filter(([_, item]) => item.quality === "t3").map(([id]) => parseInt(id));
|
|
797
|
+
let t2Skins = skinItems.filter(([_, item]) => item.quality === "t2").map(([id]) => parseInt(id));
|
|
798
|
+
let t1Skins = skinItems.filter(([_, item]) => item.quality === "t1").map(([id]) => parseInt(id));
|
|
799
|
+
t3Skins = t3Skins.filter((id) => !ownedItems.has(id));
|
|
800
|
+
t2Skins = t2Skins.filter((id) => !ownedItems.has(id));
|
|
801
|
+
t1Skins = t1Skins.filter((id) => !ownedItems.has(id));
|
|
802
|
+
if (t3Skins.length === 0 && t2Skins.length === 0 && t1Skins.length === 0) {
|
|
803
|
+
rewards.push({ itemId: 0, count: 0 });
|
|
804
|
+
nothingCount++;
|
|
805
|
+
} else {
|
|
806
|
+
const rand = Math.random() * 100;
|
|
807
|
+
let prizeId;
|
|
808
|
+
if (t3Skins.length > 0 && rand < 70) {
|
|
809
|
+
prizeId = t3Skins[Math.floor(Math.random() * t3Skins.length)];
|
|
810
|
+
} else if (t2Skins.length > 0 && rand < 90) {
|
|
811
|
+
prizeId = t2Skins[Math.floor(Math.random() * t2Skins.length)];
|
|
812
|
+
} else if (t1Skins.length > 0) {
|
|
813
|
+
prizeId = t1Skins[Math.floor(Math.random() * t1Skins.length)];
|
|
814
|
+
gotSSR = true;
|
|
815
|
+
rareHitCount++;
|
|
816
|
+
} else if (t2Skins.length > 0) {
|
|
817
|
+
prizeId = t2Skins[Math.floor(Math.random() * t2Skins.length)];
|
|
818
|
+
} else if (t3Skins.length > 0) {
|
|
819
|
+
prizeId = t3Skins[Math.floor(Math.random() * t3Skins.length)];
|
|
820
|
+
} else {
|
|
821
|
+
prizeId = t1Skins[Math.floor(Math.random() * t1Skins.length)];
|
|
822
|
+
gotSSR = true;
|
|
823
|
+
rareHitCount++;
|
|
824
|
+
}
|
|
825
|
+
rewards.push({ itemId: prizeId, count: 1 });
|
|
826
|
+
}
|
|
827
|
+
} else if (isPetPool) {
|
|
828
|
+
const petItems = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "宠物");
|
|
829
|
+
let t3Pets = petItems.filter(([_, item]) => item.quality === "t3").map(([id]) => parseInt(id));
|
|
830
|
+
let t2Pets = petItems.filter(([_, item]) => item.quality === "t2").map(([id]) => parseInt(id));
|
|
831
|
+
let t1Pets = petItems.filter(([_, item]) => item.quality === "t1").map(([id]) => parseInt(id));
|
|
832
|
+
let t0Pets = petItems.filter(([_, item]) => item.quality === "t0").map(([id]) => parseInt(id));
|
|
833
|
+
t3Pets = t3Pets.filter((id) => !ownedItems.has(id));
|
|
834
|
+
t2Pets = t2Pets.filter((id) => !ownedItems.has(id));
|
|
835
|
+
t1Pets = t1Pets.filter((id) => !ownedItems.has(id));
|
|
836
|
+
t0Pets = t0Pets.filter((id) => !ownedItems.has(id));
|
|
837
|
+
if (t3Pets.length === 0 && t2Pets.length === 0 && t1Pets.length === 0 && t0Pets.length === 0) {
|
|
838
|
+
rewards.push({ itemId: 0, count: 0 });
|
|
839
|
+
nothingCount++;
|
|
840
|
+
} else {
|
|
841
|
+
const rand = Math.random() * 100;
|
|
842
|
+
let prizeId;
|
|
843
|
+
if (t3Pets.length > 0 && rand < 65) {
|
|
844
|
+
prizeId = t3Pets[Math.floor(Math.random() * t3Pets.length)];
|
|
845
|
+
} else if (t2Pets.length > 0 && rand < 85) {
|
|
846
|
+
prizeId = t2Pets[Math.floor(Math.random() * t2Pets.length)];
|
|
847
|
+
} else if (t1Pets.length > 0 && rand < 95) {
|
|
848
|
+
prizeId = t1Pets[Math.floor(Math.random() * t1Pets.length)];
|
|
849
|
+
} else if (t0Pets.length > 0) {
|
|
850
|
+
prizeId = t0Pets[Math.floor(Math.random() * t0Pets.length)];
|
|
851
|
+
gotSSR = true;
|
|
852
|
+
rareHitCount++;
|
|
853
|
+
} else if (t1Pets.length > 0) {
|
|
854
|
+
prizeId = t1Pets[Math.floor(Math.random() * t1Pets.length)];
|
|
855
|
+
} else if (t2Pets.length > 0) {
|
|
856
|
+
prizeId = t2Pets[Math.floor(Math.random() * t2Pets.length)];
|
|
857
|
+
} else if (t3Pets.length > 0) {
|
|
858
|
+
prizeId = t3Pets[Math.floor(Math.random() * t3Pets.length)];
|
|
859
|
+
} else {
|
|
860
|
+
prizeId = t0Pets[Math.floor(Math.random() * t0Pets.length)];
|
|
861
|
+
gotSSR = true;
|
|
862
|
+
rareHitCount++;
|
|
863
|
+
}
|
|
864
|
+
rewards.push({ itemId: prizeId, count: 1 });
|
|
865
|
+
}
|
|
866
|
+
} else {
|
|
867
|
+
if (pityCounter >= 90) {
|
|
868
|
+
rewards.push({ itemId: 3, count: 1 });
|
|
869
|
+
totalCoupon += 1;
|
|
870
|
+
gotSSR = true;
|
|
871
|
+
pityCounter = 0;
|
|
872
|
+
rareHitCount++;
|
|
873
|
+
pityTriggered = true;
|
|
874
|
+
} else {
|
|
875
|
+
const rand = Math.random() * 100;
|
|
876
|
+
if (rand < 70) {
|
|
877
|
+
rewards.push({ itemId: 1, count: 10 });
|
|
878
|
+
totalGold += 10;
|
|
879
|
+
} else if (rand < 90) {
|
|
880
|
+
rewards.push({ itemId: 1, count: 20 });
|
|
881
|
+
totalGold += 20;
|
|
882
|
+
} else if (rand < 98) {
|
|
883
|
+
rewards.push({ itemId: 1, count: 50 });
|
|
884
|
+
totalGold += 50;
|
|
885
|
+
} else if (rand < 99.5) {
|
|
886
|
+
rewards.push({ itemId: 1, count: 100 });
|
|
887
|
+
totalGold += 100;
|
|
888
|
+
} else {
|
|
889
|
+
rewards.push({ itemId: 3, count: 1 });
|
|
890
|
+
totalCoupon += 1;
|
|
891
|
+
gotSSR = true;
|
|
892
|
+
pityCounter = 0;
|
|
893
|
+
rareHitCount++;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
const newCostCount = costItemCount - drawCount * costPerDraw;
|
|
899
|
+
if (costItem) {
|
|
900
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
901
|
+
user_id: handle,
|
|
902
|
+
item_id: costItemId,
|
|
903
|
+
count: newCostCount
|
|
904
|
+
}]);
|
|
905
|
+
}
|
|
906
|
+
if (totalGold > 0) {
|
|
907
|
+
const [goldItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 1 });
|
|
908
|
+
const newGoldCount = (goldItem?.count || 0) + totalGold;
|
|
909
|
+
if (goldItem) {
|
|
910
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
911
|
+
user_id: handle,
|
|
912
|
+
item_id: 1,
|
|
913
|
+
count: newGoldCount
|
|
914
|
+
}]);
|
|
915
|
+
} else {
|
|
916
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
917
|
+
user_id: handle,
|
|
918
|
+
item_id: 1,
|
|
919
|
+
count: newGoldCount
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (totalCoupon > 0) {
|
|
924
|
+
const [couponItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 3 });
|
|
925
|
+
const newCouponCount = (couponItem?.count || 0) + totalCoupon;
|
|
926
|
+
if (couponItem) {
|
|
927
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
928
|
+
user_id: handle,
|
|
929
|
+
item_id: 3,
|
|
930
|
+
count: newCouponCount
|
|
931
|
+
}]);
|
|
932
|
+
} else {
|
|
933
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
934
|
+
user_id: handle,
|
|
935
|
+
item_id: 3,
|
|
936
|
+
count: newCouponCount
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (totalGugub > 0) {
|
|
941
|
+
const [gugubItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 2 });
|
|
942
|
+
const newGugubCount = (gugubItem?.count || 0) + totalGugub;
|
|
943
|
+
if (gugubItem) {
|
|
944
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
945
|
+
user_id: handle,
|
|
946
|
+
item_id: 2,
|
|
947
|
+
count: newGugubCount
|
|
948
|
+
}]);
|
|
949
|
+
} else {
|
|
950
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
951
|
+
user_id: handle,
|
|
952
|
+
item_id: 2,
|
|
953
|
+
count: newGugubCount
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (totalMakeupCoupon > 0) {
|
|
958
|
+
const [makeupItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 9 });
|
|
959
|
+
const newMakeupCount = (makeupItem?.count || 0) + totalMakeupCoupon;
|
|
960
|
+
if (makeupItem) {
|
|
961
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
962
|
+
user_id: handle,
|
|
963
|
+
item_id: 9,
|
|
964
|
+
count: newMakeupCount
|
|
965
|
+
}]);
|
|
966
|
+
} else {
|
|
967
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
968
|
+
user_id: handle,
|
|
969
|
+
item_id: 9,
|
|
970
|
+
count: newMakeupCount
|
|
971
|
+
});
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (isSkinPool || isPetPool) {
|
|
975
|
+
for (const reward of rewards) {
|
|
976
|
+
if (reward.itemId === 0 || reward.count === 0) continue;
|
|
977
|
+
const [existingItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: reward.itemId });
|
|
978
|
+
const newCount = (existingItem?.count || 0) + reward.count;
|
|
979
|
+
if (existingItem) {
|
|
980
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
981
|
+
user_id: handle,
|
|
982
|
+
item_id: reward.itemId,
|
|
983
|
+
count: newCount
|
|
984
|
+
}]);
|
|
985
|
+
} else {
|
|
986
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
987
|
+
user_id: handle,
|
|
988
|
+
item_id: reward.itemId,
|
|
989
|
+
count: newCount
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
await ctx.database.create("ggcevo_exchange_log", {
|
|
993
|
+
user_id: handle,
|
|
994
|
+
exchange_id: reward.itemId,
|
|
995
|
+
cost_type: 2,
|
|
996
|
+
create_time: now
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
let costTypeName;
|
|
1001
|
+
if (isGoldPool) {
|
|
1002
|
+
costTypeName = 1;
|
|
1003
|
+
} else if (isSkinPool || isPetPool) {
|
|
1004
|
+
costTypeName = 1;
|
|
1005
|
+
} else {
|
|
1006
|
+
costTypeName = 2;
|
|
1007
|
+
}
|
|
1008
|
+
for (let i = 0; i < drawCount; i++) {
|
|
1009
|
+
const reward = rewards[i] || { itemId: 0, count: 0 };
|
|
1010
|
+
await ctx.database.create("ggcevo_lottery_log", {
|
|
1011
|
+
user_id: handle,
|
|
1012
|
+
lottery_id: poolId,
|
|
1013
|
+
prize_id: reward.itemId,
|
|
1014
|
+
cost_type: costTypeName,
|
|
1015
|
+
cost_num: costPerDraw,
|
|
1016
|
+
draw_time: now
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
const newTotalDrawCount = (lotteryStatus?.total_draw_count || 0) + drawCount;
|
|
1020
|
+
const newPityTriggeredCount = (lotteryStatus?.pity_triggered_count || 0) + (pityTriggered ? 1 : 0);
|
|
1021
|
+
if (lotteryStatus) {
|
|
1022
|
+
await ctx.database.upsert("ggcevo_lottery_status", [{
|
|
1023
|
+
user_id: handle,
|
|
1024
|
+
lottery_id: poolId,
|
|
1025
|
+
pity_counter: pityCounter,
|
|
1026
|
+
total_draw_count: newTotalDrawCount,
|
|
1027
|
+
pity_triggered_count: newPityTriggeredCount,
|
|
1028
|
+
rare_hit_count: rareHitCount,
|
|
1029
|
+
update_time: now
|
|
1030
|
+
}]);
|
|
1031
|
+
} else {
|
|
1032
|
+
await ctx.database.create("ggcevo_lottery_status", {
|
|
1033
|
+
user_id: handle,
|
|
1034
|
+
lottery_id: poolId,
|
|
1035
|
+
pity_counter: pityCounter,
|
|
1036
|
+
total_draw_count: newTotalDrawCount,
|
|
1037
|
+
pity_triggered_count: newPityTriggeredCount,
|
|
1038
|
+
rare_hit_count: rareHitCount,
|
|
1039
|
+
update_time: now
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
let costItemName;
|
|
1043
|
+
if (isGoldPool) {
|
|
1044
|
+
costItemName = "金币";
|
|
1045
|
+
} else if (isSkinPool || isPetPool) {
|
|
1046
|
+
costItemName = "兑换券";
|
|
1047
|
+
} else {
|
|
1048
|
+
costItemName = "咕咕币";
|
|
1049
|
+
}
|
|
1050
|
+
let result = `🎰 使用 ${drawCount * costPerDraw} ${costItemName}进行了 ${drawCount} 次${poolName}抽奖!
|
|
1051
|
+
`;
|
|
1052
|
+
if (isSkinPool) {
|
|
1053
|
+
const skinRewards = [];
|
|
1054
|
+
for (const reward of rewards) {
|
|
1055
|
+
const item = ExchangeConfig[reward.itemId];
|
|
1056
|
+
if (item) {
|
|
1057
|
+
skinRewards.push({ name: item.name, quality: item.quality });
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
if (skinRewards.length === 0 && nothingCount > 0) {
|
|
1061
|
+
result += `💨 获得的物品均已拥有
|
|
1062
|
+
`;
|
|
1063
|
+
}
|
|
1064
|
+
for (const skin of skinRewards) {
|
|
1065
|
+
let qualityEmoji = "";
|
|
1066
|
+
switch (skin.quality) {
|
|
1067
|
+
case "t1":
|
|
1068
|
+
qualityEmoji = "⭐";
|
|
1069
|
+
break;
|
|
1070
|
+
case "t2":
|
|
1071
|
+
qualityEmoji = "✨";
|
|
1072
|
+
break;
|
|
1073
|
+
case "t3":
|
|
1074
|
+
qualityEmoji = "💫";
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
result += `${qualityEmoji} 获得 ${skin.name}(${skin.quality})
|
|
1078
|
+
`;
|
|
1079
|
+
}
|
|
1080
|
+
} else if (isPetPool) {
|
|
1081
|
+
const petRewards = [];
|
|
1082
|
+
for (const reward of rewards) {
|
|
1083
|
+
const item = ExchangeConfig[reward.itemId];
|
|
1084
|
+
if (item) {
|
|
1085
|
+
petRewards.push({ name: item.name, quality: item.quality });
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
if (petRewards.length === 0 && nothingCount > 0) {
|
|
1089
|
+
result += `💨 获得的物品均已拥有
|
|
1090
|
+
`;
|
|
1091
|
+
}
|
|
1092
|
+
for (const pet of petRewards) {
|
|
1093
|
+
let qualityEmoji = "";
|
|
1094
|
+
switch (pet.quality) {
|
|
1095
|
+
case "t0":
|
|
1096
|
+
qualityEmoji = "👑";
|
|
1097
|
+
break;
|
|
1098
|
+
case "t1":
|
|
1099
|
+
qualityEmoji = "⭐";
|
|
1100
|
+
break;
|
|
1101
|
+
case "t2":
|
|
1102
|
+
qualityEmoji = "✨";
|
|
1103
|
+
break;
|
|
1104
|
+
case "t3":
|
|
1105
|
+
qualityEmoji = "💫";
|
|
1106
|
+
break;
|
|
1107
|
+
}
|
|
1108
|
+
result += `${qualityEmoji} 获得 ${pet.name}(${pet.quality})
|
|
1109
|
+
`;
|
|
1110
|
+
}
|
|
1111
|
+
} else {
|
|
1112
|
+
if (totalGold > 0) result += `💰 获得 ${totalGold} 金币
|
|
1113
|
+
`;
|
|
1114
|
+
if (totalGugub > 0) result += `🪙 获得 ${totalGugub} 咕咕币
|
|
1115
|
+
`;
|
|
1116
|
+
if (totalCoupon > 0) result += `🎫 获得 ${totalCoupon} 兑换券
|
|
1117
|
+
`;
|
|
1118
|
+
if (totalMakeupCoupon > 0) result += `🎟️ 获得 ${totalMakeupCoupon} 补签券
|
|
1119
|
+
`;
|
|
1120
|
+
if (nothingCount > 0) result += `💨 ${nothingCount} 次未获得物品
|
|
1121
|
+
`;
|
|
1122
|
+
}
|
|
1123
|
+
result += `累计抽奖次数:${newTotalDrawCount}`;
|
|
1124
|
+
if (!isGoldPool && !isSkinPool && !isPetPool) {
|
|
1125
|
+
result += `
|
|
1126
|
+
🔮 保底进度:${pityCounter}/90`;
|
|
1127
|
+
}
|
|
1128
|
+
return result;
|
|
1129
|
+
});
|
|
1130
|
+
ctx.command("ggcevo/背包").action(async (argv) => {
|
|
1131
|
+
const session = argv.session;
|
|
1132
|
+
const handle = await getHandle(session);
|
|
1133
|
+
if (!handle) {
|
|
1134
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
1135
|
+
}
|
|
1136
|
+
const backpackItems = await ctx.database.get("ggcevo_backpack", { user_id: handle });
|
|
1137
|
+
if (backpackItems.length === 0) {
|
|
1138
|
+
return "🎒 背包是空的,快去签到或抽奖获得物品吧!";
|
|
1139
|
+
}
|
|
1140
|
+
let message = `🎒 ${session.username}的背包物品:
|
|
1141
|
+
`;
|
|
1142
|
+
for (const item of backpackItems) {
|
|
1143
|
+
const itemName = ItemConfig[item.item_id];
|
|
1144
|
+
if (!itemName) continue;
|
|
1145
|
+
message += `${itemName} x${item.count}
|
|
1146
|
+
`;
|
|
1147
|
+
}
|
|
1148
|
+
return message;
|
|
1149
|
+
});
|
|
1150
|
+
ctx.command("ggcevo/个人信息").action(async (argv) => {
|
|
1151
|
+
const session = argv.session;
|
|
1152
|
+
const handle = await getHandle(session);
|
|
1153
|
+
if (!handle) {
|
|
1154
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
1155
|
+
}
|
|
1156
|
+
const username = session.username || "未知用户";
|
|
1157
|
+
let message = `👤 个人信息
|
|
1158
|
+
`;
|
|
1159
|
+
message += `─────────────
|
|
1160
|
+
`;
|
|
1161
|
+
message += `用户名:${username}
|
|
1162
|
+
`;
|
|
1163
|
+
message += `游戏句柄:${handle}
|
|
1164
|
+
`;
|
|
1165
|
+
const [signinSummary] = await ctx.database.get("ggcevo_signin_summary", { user_id: handle });
|
|
1166
|
+
if (signinSummary) {
|
|
1167
|
+
const lastSigninDate = signinSummary.last_signin_date ? new Date(signinSummary.last_signin_date).toLocaleDateString("zh-CN") : "从未签到";
|
|
1168
|
+
message += `─────────────
|
|
1169
|
+
`;
|
|
1170
|
+
message += `📅 签到信息
|
|
1171
|
+
`;
|
|
1172
|
+
message += `累计签到:${signinSummary.total_days} 天
|
|
1173
|
+
`;
|
|
1174
|
+
message += `本月签到:${signinSummary.month_days} 天
|
|
1175
|
+
`;
|
|
1176
|
+
message += `连续签到:${signinSummary.continuous_days} 天
|
|
1177
|
+
`;
|
|
1178
|
+
message += `最后签到:${lastSigninDate}
|
|
1179
|
+
`;
|
|
1180
|
+
} else {
|
|
1181
|
+
message += `─────────────
|
|
1182
|
+
`;
|
|
1183
|
+
message += `📅 签到信息:暂无签到记录
|
|
1184
|
+
`;
|
|
1185
|
+
}
|
|
1186
|
+
const lotteryStatuses = await ctx.database.get("ggcevo_lottery_status", { user_id: handle });
|
|
1187
|
+
if (lotteryStatuses.length > 0) {
|
|
1188
|
+
message += `─────────────
|
|
1189
|
+
`;
|
|
1190
|
+
message += `🎰 抽奖信息
|
|
1191
|
+
`;
|
|
1192
|
+
for (const status of lotteryStatuses) {
|
|
1193
|
+
const poolName = LotteryPoolConfig[status.lottery_id] || "未知奖池";
|
|
1194
|
+
message += `【${poolName}】
|
|
1195
|
+
`;
|
|
1196
|
+
message += ` 累计抽奖:${status.total_draw_count} 次
|
|
1197
|
+
`;
|
|
1198
|
+
if (status.lottery_id === 2) {
|
|
1199
|
+
message += ` 🔮 保底进度:${status.pity_counter}/90
|
|
1200
|
+
`;
|
|
1201
|
+
message += ` 保底触发:${status.pity_triggered_count} 次
|
|
1202
|
+
`;
|
|
1203
|
+
message += ` 稀有命中:${status.rare_hit_count} 次
|
|
1204
|
+
`;
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
} else {
|
|
1208
|
+
message += `─────────────
|
|
1209
|
+
`;
|
|
1210
|
+
message += `🎰 抽奖信息:暂无抽奖记录
|
|
1211
|
+
`;
|
|
1212
|
+
}
|
|
1213
|
+
const allExchangeLogs = await ctx.database.get("ggcevo_exchange_log", { user_id: handle });
|
|
1214
|
+
const exchangeLogs = allExchangeLogs.filter((log) => log.cost_type !== 3 && log.cost_type !== 4);
|
|
1215
|
+
if (exchangeLogs.length > 0) {
|
|
1216
|
+
message += `─────────────
|
|
1217
|
+
`;
|
|
1218
|
+
message += `🎁 兑换记录
|
|
1219
|
+
`;
|
|
1220
|
+
const sortedLogs = exchangeLogs.sort(
|
|
1221
|
+
(a, b) => new Date(b.create_time).getTime() - new Date(a.create_time).getTime()
|
|
1222
|
+
).slice(0, 10);
|
|
1223
|
+
for (const log of sortedLogs) {
|
|
1224
|
+
const itemName = ExchangeConfig[log.exchange_id]?.name || "未知物品";
|
|
1225
|
+
const costType = log.cost_type === 1 ? "兑换" : log.cost_type === 2 ? "抽奖获得" : "其他";
|
|
1226
|
+
const createTime = new Date(log.create_time).toLocaleDateString("zh-CN");
|
|
1227
|
+
message += `${createTime} ${itemName}(${costType})
|
|
1228
|
+
`;
|
|
1229
|
+
}
|
|
1230
|
+
} else {
|
|
1231
|
+
message += `─────────────
|
|
1232
|
+
`;
|
|
1233
|
+
message += `🎁 兑换记录:暂无兑换记录
|
|
1234
|
+
`;
|
|
1235
|
+
}
|
|
1236
|
+
const atonementLogs = allExchangeLogs.filter((log) => log.cost_type === 4);
|
|
1237
|
+
if (atonementLogs.length > 0) {
|
|
1238
|
+
message += `─────────────
|
|
1239
|
+
`;
|
|
1240
|
+
message += `📿 赎罪券使用记录
|
|
1241
|
+
`;
|
|
1242
|
+
const sortedAtonementLogs = atonementLogs.sort(
|
|
1243
|
+
(a, b) => new Date(b.create_time).getTime() - new Date(a.create_time).getTime()
|
|
1244
|
+
).slice(0, 3);
|
|
1245
|
+
for (const log of sortedAtonementLogs) {
|
|
1246
|
+
const createTime = new Date(log.create_time).toLocaleString("zh-CN");
|
|
1247
|
+
message += ` ${createTime}
|
|
1248
|
+
`;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return message;
|
|
1252
|
+
});
|
|
1253
|
+
ctx.command("ggcevo/给予 <targetId> <itemId> <count:number>", { authority: 3 }).action(async (argv, targetId, itemId, count) => {
|
|
1254
|
+
const session = argv.session;
|
|
1255
|
+
let resolvedTargetId = targetId;
|
|
1256
|
+
const atElements = session.elements?.filter((e) => e.type === "at");
|
|
1257
|
+
if (atElements && atElements.length > 0) {
|
|
1258
|
+
const mentionedUserId = atElements[0].attrs.id;
|
|
1259
|
+
const [profile] = await ctx.database.get("sc2arcade_player", { userId: mentionedUserId, isActive: true });
|
|
1260
|
+
if (profile) {
|
|
1261
|
+
resolvedTargetId = `${profile.regionId}-S2-${profile.realmId}-${profile.profileId}`;
|
|
1262
|
+
} else {
|
|
1263
|
+
return "❌ @提及的用户未绑定游戏句柄!";
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
let resolvedItemId;
|
|
1267
|
+
const parsedId = parseInt(itemId);
|
|
1268
|
+
if (!isNaN(parsedId)) {
|
|
1269
|
+
resolvedItemId = parsedId;
|
|
1270
|
+
} else {
|
|
1271
|
+
const itemConfigEntry = Object.entries(ItemConfig).find(([_, name2]) => name2 === itemId);
|
|
1272
|
+
if (itemConfigEntry) {
|
|
1273
|
+
resolvedItemId = parseInt(itemConfigEntry[0]);
|
|
1274
|
+
} else {
|
|
1275
|
+
const exchangeConfigEntry = Object.entries(ExchangeConfig).find(([_, item]) => item.name === itemId);
|
|
1276
|
+
if (exchangeConfigEntry) {
|
|
1277
|
+
resolvedItemId = parseInt(exchangeConfigEntry[0]);
|
|
1278
|
+
} else {
|
|
1279
|
+
return `❌ 未找到名为 "${itemId}" 的物品!`;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
const itemName = ItemConfig[resolvedItemId] || ExchangeConfig[resolvedItemId]?.name;
|
|
1284
|
+
if (!itemName) {
|
|
1285
|
+
return `❌ 不存在ID为 ${resolvedItemId} 的物品!`;
|
|
1286
|
+
}
|
|
1287
|
+
if (count <= 0) {
|
|
1288
|
+
return "❌ 数量必须大于0!";
|
|
1289
|
+
}
|
|
1290
|
+
const [targetItem] = await ctx.database.get("ggcevo_backpack", { user_id: resolvedTargetId, item_id: resolvedItemId });
|
|
1291
|
+
const targetCount = (targetItem?.count || 0) + count;
|
|
1292
|
+
if (targetItem) {
|
|
1293
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
1294
|
+
user_id: resolvedTargetId,
|
|
1295
|
+
item_id: resolvedItemId,
|
|
1296
|
+
count: targetCount
|
|
1297
|
+
}]);
|
|
1298
|
+
} else {
|
|
1299
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
1300
|
+
user_id: resolvedTargetId,
|
|
1301
|
+
item_id: resolvedItemId,
|
|
1302
|
+
count: targetCount
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
return `✅ 成功给予 ${resolvedTargetId} ${count} 个${itemName}!`;
|
|
1306
|
+
});
|
|
1307
|
+
ctx.command("ggcevo/抽奖概率").action(async () => {
|
|
1308
|
+
let message = `🎰 抽奖概率说明
|
|
1309
|
+
`;
|
|
1310
|
+
message += `─────────────
|
|
1311
|
+
`;
|
|
1312
|
+
message += `【使用方法】
|
|
1313
|
+
`;
|
|
1314
|
+
message += ` 抽奖 [-p 奖池ID] [-c 次数]
|
|
1315
|
+
`;
|
|
1316
|
+
message += ` -p 指定奖池(默认普通池):1=金币池,2=普通池,3=皮肤池,4=宠物池
|
|
1317
|
+
`;
|
|
1318
|
+
message += ` -c 指定抽奖次数(金币池默认10连抽,普通池默认全抽,皮肤/宠物池默认单抽)
|
|
1319
|
+
`;
|
|
1320
|
+
message += `─────────────
|
|
1321
|
+
`;
|
|
1322
|
+
message += `【金币池】ID:1 消耗:100金币/次
|
|
1323
|
+
`;
|
|
1324
|
+
message += ` 20% 空手而归
|
|
1325
|
+
`;
|
|
1326
|
+
message += ` 50% 获得 80 金币
|
|
1327
|
+
`;
|
|
1328
|
+
message += ` 15% 获得 150 金币
|
|
1329
|
+
`;
|
|
1330
|
+
message += ` 10% 获得 咕咕币 x1
|
|
1331
|
+
`;
|
|
1332
|
+
message += ` 5% 获得 补签券 x1
|
|
1333
|
+
`;
|
|
1334
|
+
message += `─────────────
|
|
1335
|
+
`;
|
|
1336
|
+
message += `【普通池】ID:2 消耗:1咕咕币/次
|
|
1337
|
+
`;
|
|
1338
|
+
message += ` 保底:90次必出 兑换券
|
|
1339
|
+
`;
|
|
1340
|
+
message += ` 70% 获得 10 金币
|
|
1341
|
+
`;
|
|
1342
|
+
message += ` 20% 获得 20 金币
|
|
1343
|
+
`;
|
|
1344
|
+
message += ` 8% 获得 50 金币
|
|
1345
|
+
`;
|
|
1346
|
+
message += ` 1.5% 获得 100 金币
|
|
1347
|
+
`;
|
|
1348
|
+
message += ` 0.5% 获得 兑换券
|
|
1349
|
+
`;
|
|
1350
|
+
message += `─────────────
|
|
1351
|
+
`;
|
|
1352
|
+
message += `【皮肤池】ID:3 消耗:3兑换券/次
|
|
1353
|
+
`;
|
|
1354
|
+
message += ` 70% 获得 T3 皮肤
|
|
1355
|
+
`;
|
|
1356
|
+
message += ` 20% 获得 T2 皮肤
|
|
1357
|
+
`;
|
|
1358
|
+
message += ` 10% 获得 T1 皮肤
|
|
1359
|
+
`;
|
|
1360
|
+
message += ` 不会抽到已拥有的物品
|
|
1361
|
+
`;
|
|
1362
|
+
message += `─────────────
|
|
1363
|
+
`;
|
|
1364
|
+
message += `【宠物池】ID:4 消耗:3兑换券/次
|
|
1365
|
+
`;
|
|
1366
|
+
message += ` 65% 获得 T3 宠物
|
|
1367
|
+
`;
|
|
1368
|
+
message += ` 20% 获得 T2 宠物
|
|
1369
|
+
`;
|
|
1370
|
+
message += ` 10% 获得 T1 宠物
|
|
1371
|
+
`;
|
|
1372
|
+
message += ` 5% 获得 T0 宠物
|
|
1373
|
+
`;
|
|
1374
|
+
message += ` 不会抽到已拥有的物品
|
|
1375
|
+
`;
|
|
1376
|
+
return message;
|
|
1377
|
+
});
|
|
1378
|
+
ctx.command("ggcevo/签到奖励").action(async () => {
|
|
1379
|
+
let message = `📅 签到奖励说明
|
|
1380
|
+
`;
|
|
1381
|
+
message += `─────────────
|
|
1382
|
+
`;
|
|
1383
|
+
message += `【每日基础奖励】
|
|
1384
|
+
`;
|
|
1385
|
+
message += ` 金币:10~20(随机)
|
|
1386
|
+
`;
|
|
1387
|
+
message += ` 咕咕币:3
|
|
1388
|
+
`;
|
|
1389
|
+
message += `─────────────
|
|
1390
|
+
`;
|
|
1391
|
+
message += `【本月累计签到额外奖励】
|
|
1392
|
+
`;
|
|
1393
|
+
message += ` 本月第7天:+1 咕咕币
|
|
1394
|
+
`;
|
|
1395
|
+
message += ` 本月第14天:+2 咕咕币
|
|
1396
|
+
`;
|
|
1397
|
+
message += ` 本月第21天:+3 咕咕币
|
|
1398
|
+
`;
|
|
1399
|
+
message += ` 本月第28天:+4 咕咕币
|
|
1400
|
+
`;
|
|
1401
|
+
message += `─────────────
|
|
1402
|
+
`;
|
|
1403
|
+
message += `【每月津贴】
|
|
1404
|
+
`;
|
|
1405
|
+
message += ` 仅管理员/群主可领取
|
|
1406
|
+
`;
|
|
1407
|
+
message += ` 每月首次签到额外奖励:+50 咕咕币
|
|
1408
|
+
`;
|
|
1409
|
+
return message;
|
|
1410
|
+
});
|
|
1411
|
+
ctx.command("ggcevo/兑换列表").alias("兑换表").action(async () => {
|
|
1412
|
+
let message = `🎁 可兑换物品列表
|
|
1413
|
+
`;
|
|
1414
|
+
message += `─────────────
|
|
1415
|
+
`;
|
|
1416
|
+
message += `【T3 皮肤】消耗:3 兑换券
|
|
1417
|
+
`;
|
|
1418
|
+
const t3Skins = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "皮肤" && item.quality === "t3");
|
|
1419
|
+
for (const [id, item] of t3Skins) {
|
|
1420
|
+
message += ` ${item.name}
|
|
1421
|
+
`;
|
|
1422
|
+
}
|
|
1423
|
+
message += `─────────────
|
|
1424
|
+
`;
|
|
1425
|
+
message += `【T2 皮肤】消耗:4 兑换券
|
|
1426
|
+
`;
|
|
1427
|
+
const t2Skins = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "皮肤" && item.quality === "t2");
|
|
1428
|
+
for (const [id, item] of t2Skins) {
|
|
1429
|
+
message += ` ${item.name}
|
|
1430
|
+
`;
|
|
1431
|
+
}
|
|
1432
|
+
message += `─────────────
|
|
1433
|
+
`;
|
|
1434
|
+
message += `【T1 皮肤】消耗:5 兑换券
|
|
1435
|
+
`;
|
|
1436
|
+
const t1Skins = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "皮肤" && item.quality === "t1");
|
|
1437
|
+
for (const [id, item] of t1Skins) {
|
|
1438
|
+
message += ` ${item.name}
|
|
1439
|
+
`;
|
|
1440
|
+
}
|
|
1441
|
+
message += `─────────────
|
|
1442
|
+
`;
|
|
1443
|
+
message += `【T3 宠物】消耗:3 兑换券
|
|
1444
|
+
`;
|
|
1445
|
+
const t3Pets = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "宠物" && item.quality === "t3");
|
|
1446
|
+
for (const [id, item] of t3Pets) {
|
|
1447
|
+
message += ` ${item.name}
|
|
1448
|
+
`;
|
|
1449
|
+
}
|
|
1450
|
+
message += `─────────────
|
|
1451
|
+
`;
|
|
1452
|
+
message += `【T2 宠物】消耗:4 兑换券
|
|
1453
|
+
`;
|
|
1454
|
+
const t2Pets = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "宠物" && item.quality === "t2");
|
|
1455
|
+
for (const [id, item] of t2Pets) {
|
|
1456
|
+
message += ` ${item.name}
|
|
1457
|
+
`;
|
|
1458
|
+
}
|
|
1459
|
+
message += `─────────────
|
|
1460
|
+
`;
|
|
1461
|
+
message += `【T1 宠物】消耗:5 兑换券
|
|
1462
|
+
`;
|
|
1463
|
+
const t1Pets = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "宠物" && item.quality === "t1");
|
|
1464
|
+
for (const [id, item] of t1Pets) {
|
|
1465
|
+
message += ` ${item.name}
|
|
1466
|
+
`;
|
|
1467
|
+
}
|
|
1468
|
+
message += `─────────────
|
|
1469
|
+
`;
|
|
1470
|
+
message += `【T0 宠物】消耗:6 兑换券
|
|
1471
|
+
`;
|
|
1472
|
+
const t0Pets = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "宠物" && item.quality === "t0");
|
|
1473
|
+
for (const [id, item] of t0Pets) {
|
|
1474
|
+
message += ` ${item.name}
|
|
1475
|
+
`;
|
|
1476
|
+
}
|
|
1477
|
+
message += `─────────────
|
|
1478
|
+
`;
|
|
1479
|
+
message += `【入场特效】消耗:5 兑换券
|
|
1480
|
+
`;
|
|
1481
|
+
const effects = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "入场特效");
|
|
1482
|
+
for (const [id, item] of effects) {
|
|
1483
|
+
message += ` ${item.name}
|
|
1484
|
+
`;
|
|
1485
|
+
}
|
|
1486
|
+
message += `─────────────
|
|
1487
|
+
`;
|
|
1488
|
+
message += `【角色名称】
|
|
1489
|
+
`;
|
|
1490
|
+
const costMap = { "t3": 3, "t2": 4, "t1": 5, "t0": 6 };
|
|
1491
|
+
const roleNames = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "角色名称");
|
|
1492
|
+
for (const [id, item] of roleNames) {
|
|
1493
|
+
const cost = item.cost ?? costMap[item.quality];
|
|
1494
|
+
message += ` ${item.name}(消耗:${cost} 兑换券)
|
|
1495
|
+
`;
|
|
1496
|
+
}
|
|
1497
|
+
message += `─────────────
|
|
1498
|
+
`;
|
|
1499
|
+
message += `【物品】
|
|
1500
|
+
`;
|
|
1501
|
+
const items = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "物品");
|
|
1502
|
+
for (const [id, item] of items) {
|
|
1503
|
+
const cost = item.cost ?? costMap[item.quality];
|
|
1504
|
+
message += ` ${item.name}(消耗:${cost} 兑换券)
|
|
1505
|
+
`;
|
|
1506
|
+
}
|
|
1507
|
+
message += `─────────────
|
|
1508
|
+
`;
|
|
1509
|
+
message += `【道具】
|
|
1510
|
+
`;
|
|
1511
|
+
const props = Object.entries(ExchangeConfig).filter(([_, item]) => item.type === "道具");
|
|
1512
|
+
for (const [id, item] of props) {
|
|
1513
|
+
const cost = item.cost ?? costMap[item.quality];
|
|
1514
|
+
message += ` ${item.name}(消耗:${cost} 兑换券)
|
|
1515
|
+
`;
|
|
1516
|
+
}
|
|
1517
|
+
message += `─────────────
|
|
1518
|
+
`;
|
|
1519
|
+
message += `⚠️ 限定物品不可兑换
|
|
1520
|
+
`;
|
|
1521
|
+
message += `⚠️ 兑换功能仅限工作日使用(周一至周五)
|
|
1522
|
+
`;
|
|
1523
|
+
message += `⚠️ 兑换完成后请找活动管理员登记
|
|
1524
|
+
`;
|
|
1525
|
+
message += `⚠️ 兑换实装进咕咕虫将在休息日完成(周六/周日)
|
|
1526
|
+
`;
|
|
1527
|
+
return message;
|
|
1528
|
+
});
|
|
1529
|
+
ctx.command("ggcevo/创建活动", { authority: 3 }).option("name", "-n <name:string> 活动名称").option("description", "-d <description:string> 活动描述").option("rewardItem", "-r <rewardItem:number> 奖励物品ID").option("rewardAmount", "-a <rewardAmount:number> 奖励数量").option("startTime", "-s <startTime:string> 开始时间 (格式: YYYY-MM-DD)").option("endTime", "-e <endTime:string> 结束时间 (格式: YYYY-MM-DD)").option("maxClaims", "-m <maxClaims:number> 总领取上限 (0=无限制)").option("requiredGroup", "-g <requiredGroup:string> 限制领取的群聊ID (空=无限制)").action(async (argv) => {
|
|
1530
|
+
const { options } = argv;
|
|
1531
|
+
if (!options.name || !options.description || options.rewardItem === void 0 || options.rewardAmount === void 0) {
|
|
1532
|
+
return `❌ 参数不足!
|
|
1533
|
+
格式:创建活动 -n <活动名称> -d <活动描述> -r <奖励物品ID> -a <奖励数量> [-s <开始时间>] [-e <结束时间>] [-m <领取上限>] [-g <限制群聊ID>]
|
|
1534
|
+
示例:创建活动 -n 每日签到 -d 签到领取奖励 -r 1 -a 100
|
|
1535
|
+
(开始时间默认当天,结束时间默认7天后,领取上限默认0次=无限制,群聊ID为空则无限制)`;
|
|
1536
|
+
}
|
|
1537
|
+
const rewardItemId = options.rewardItem;
|
|
1538
|
+
const itemName = ItemConfig[rewardItemId];
|
|
1539
|
+
if (!itemName) {
|
|
1540
|
+
return `❌ 不存在ID为 ${rewardItemId} 的物品!`;
|
|
1541
|
+
}
|
|
1542
|
+
const rewardAmount = options.rewardAmount;
|
|
1543
|
+
if (rewardAmount <= 0) {
|
|
1544
|
+
return `❌ 奖励数量必须大于0!`;
|
|
1545
|
+
}
|
|
1546
|
+
const maxClaims = options.maxClaims !== void 0 ? options.maxClaims : 0;
|
|
1547
|
+
const requiredGroup = options.requiredGroup || "";
|
|
1548
|
+
let startTime;
|
|
1549
|
+
let endTime;
|
|
1550
|
+
try {
|
|
1551
|
+
if (options.startTime) {
|
|
1552
|
+
const startParts = options.startTime.split("-");
|
|
1553
|
+
if (startParts.length !== 3) throw new Error("Invalid start time");
|
|
1554
|
+
startTime = new Date(parseInt(startParts[0]), parseInt(startParts[1]) - 1, parseInt(startParts[2]), 0, 0, 0);
|
|
1555
|
+
if (isNaN(startTime.getTime())) throw new Error("Invalid start time");
|
|
1556
|
+
} else {
|
|
1557
|
+
const now2 = /* @__PURE__ */ new Date();
|
|
1558
|
+
startTime = new Date(now2.getFullYear(), now2.getMonth(), now2.getDate(), 0, 0, 0);
|
|
1559
|
+
}
|
|
1560
|
+
if (options.endTime) {
|
|
1561
|
+
const endParts = options.endTime.split("-");
|
|
1562
|
+
if (endParts.length !== 3) throw new Error("Invalid end time");
|
|
1563
|
+
endTime = new Date(parseInt(endParts[0]), parseInt(endParts[1]) - 1, parseInt(endParts[2]), 23, 59, 59);
|
|
1564
|
+
if (isNaN(endTime.getTime())) throw new Error("Invalid end time");
|
|
1565
|
+
} else {
|
|
1566
|
+
endTime = new Date(startTime);
|
|
1567
|
+
endTime.setDate(endTime.getDate() + 7);
|
|
1568
|
+
endTime.setHours(23, 59, 59);
|
|
1569
|
+
}
|
|
1570
|
+
if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
|
|
1571
|
+
throw new Error("Invalid date");
|
|
1572
|
+
}
|
|
1573
|
+
} catch {
|
|
1574
|
+
return `❌ 时间格式错误!请使用格式:YYYY-MM-DD`;
|
|
1575
|
+
}
|
|
1576
|
+
if (startTime >= endTime) {
|
|
1577
|
+
return `❌ 开始时间必须早于结束时间!`;
|
|
1578
|
+
}
|
|
1579
|
+
const now = /* @__PURE__ */ new Date();
|
|
1580
|
+
await ctx.database.create("ggcevo_activity", {
|
|
1581
|
+
name: options.name,
|
|
1582
|
+
description: options.description,
|
|
1583
|
+
reward_item: rewardItemId,
|
|
1584
|
+
reward_amount: rewardAmount,
|
|
1585
|
+
start_time: startTime,
|
|
1586
|
+
end_time: endTime,
|
|
1587
|
+
max_claims: maxClaims,
|
|
1588
|
+
required_group: requiredGroup,
|
|
1589
|
+
created_at: now
|
|
1590
|
+
});
|
|
1591
|
+
const maxClaimsText = maxClaims === 0 ? "无限制" : `${maxClaims}次`;
|
|
1592
|
+
const requiredGroupText = requiredGroup === "" ? "无限制" : requiredGroup;
|
|
1593
|
+
return `✅ 活动创建成功!
|
|
1594
|
+
─────────────
|
|
1595
|
+
📛 活动名称:${options.name}
|
|
1596
|
+
📝 活动描述:${options.description}
|
|
1597
|
+
🎁 奖励物品:${itemName} x${rewardAmount}
|
|
1598
|
+
⏰ 开始时间:${startTime.toLocaleString("zh-CN")}
|
|
1599
|
+
⏰ 结束时间:${endTime.toLocaleString("zh-CN")}
|
|
1600
|
+
📊 总领取上限:${maxClaimsText}
|
|
1601
|
+
👥 限制群聊:${requiredGroupText}`;
|
|
1602
|
+
});
|
|
1603
|
+
ctx.command("ggcevo/领取活动 [activityId:number]").action(async (argv, activityId) => {
|
|
1604
|
+
const session = argv.session;
|
|
1605
|
+
const handle = await getHandle(session);
|
|
1606
|
+
if (!handle) {
|
|
1607
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
1608
|
+
}
|
|
1609
|
+
if (activityId === void 0 || activityId === null) {
|
|
1610
|
+
return `❌ 请输入活动ID!
|
|
1611
|
+
使用 活动列表 查询可领取的活动`;
|
|
1612
|
+
}
|
|
1613
|
+
const [activity] = await ctx.database.get("ggcevo_activity", { id: activityId });
|
|
1614
|
+
if (!activity) {
|
|
1615
|
+
return `❌ 不存在ID为 ${activityId} 的活动!`;
|
|
1616
|
+
}
|
|
1617
|
+
const now = /* @__PURE__ */ new Date();
|
|
1618
|
+
const startTime = new Date(activity.start_time);
|
|
1619
|
+
const endTime = new Date(activity.end_time);
|
|
1620
|
+
if (now < startTime) {
|
|
1621
|
+
return `⏰ 活动尚未开始,开始时间:${startTime.toLocaleString("zh-CN")}`;
|
|
1622
|
+
}
|
|
1623
|
+
if (now > endTime) {
|
|
1624
|
+
return `⏱️ 活动已结束,结束时间:${endTime.toLocaleString("zh-CN")}`;
|
|
1625
|
+
}
|
|
1626
|
+
if (activity.required_group && activity.required_group !== "") {
|
|
1627
|
+
const currentChannelId = session.channelId || session.event?.channel?.id;
|
|
1628
|
+
if (!currentChannelId) {
|
|
1629
|
+
return `❌ 无法获取当前群聊信息!`;
|
|
1630
|
+
}
|
|
1631
|
+
if (currentChannelId !== activity.required_group) {
|
|
1632
|
+
return `❌ 该活动仅限在指定群聊内领取!
|
|
1633
|
+
请前往群聊ID: ${activity.required_group} 领取`;
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
if (activity.max_claims > 0) {
|
|
1637
|
+
const totalClaims = await ctx.database.get("ggcevo_activity_claim_log", { activity_id: activityId });
|
|
1638
|
+
if (totalClaims.length >= activity.max_claims) {
|
|
1639
|
+
return `❌ 该活动领取次数已用尽!`;
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
const [existingClaim] = await ctx.database.get("ggcevo_activity_claim_log", {
|
|
1643
|
+
activity_id: activityId,
|
|
1644
|
+
user_id: handle
|
|
1645
|
+
});
|
|
1646
|
+
if (existingClaim) {
|
|
1647
|
+
return `❌ 您已经领取过该活动了!`;
|
|
1648
|
+
}
|
|
1649
|
+
await ctx.database.create("ggcevo_activity_claim_log", {
|
|
1650
|
+
activity_id: activityId,
|
|
1651
|
+
user_id: handle,
|
|
1652
|
+
claimed_at: now
|
|
1653
|
+
});
|
|
1654
|
+
const rewardItemName = ItemConfig[activity.reward_item] || "未知物品";
|
|
1655
|
+
const updateBackpackItem = /* @__PURE__ */ __name(async (itemId, count) => {
|
|
1656
|
+
const [existing] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: itemId });
|
|
1657
|
+
const newCount = (existing?.count || 0) + count;
|
|
1658
|
+
if (existing) {
|
|
1659
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
1660
|
+
user_id: handle,
|
|
1661
|
+
item_id: itemId,
|
|
1662
|
+
count: newCount
|
|
1663
|
+
}]);
|
|
1664
|
+
} else {
|
|
1665
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
1666
|
+
user_id: handle,
|
|
1667
|
+
item_id: itemId,
|
|
1668
|
+
count: newCount
|
|
1669
|
+
});
|
|
1670
|
+
}
|
|
1671
|
+
}, "updateBackpackItem");
|
|
1672
|
+
await updateBackpackItem(activity.reward_item, activity.reward_amount);
|
|
1673
|
+
return `🎉 领取成功!
|
|
1674
|
+
─────────────
|
|
1675
|
+
📛 活动:${activity.name}
|
|
1676
|
+
🎁 获得:${rewardItemName} x${activity.reward_amount}`;
|
|
1677
|
+
});
|
|
1678
|
+
ctx.command("ggcevo/活动列表").action(async () => {
|
|
1679
|
+
const activities = await ctx.database.get("ggcevo_activity", {});
|
|
1680
|
+
if (activities.length === 0) {
|
|
1681
|
+
return `📋 暂无活动`;
|
|
1682
|
+
}
|
|
1683
|
+
const now = /* @__PURE__ */ new Date();
|
|
1684
|
+
let message = `📋 活动列表
|
|
1685
|
+
`;
|
|
1686
|
+
message += `─────────────
|
|
1687
|
+
`;
|
|
1688
|
+
for (const activity of activities) {
|
|
1689
|
+
const startTime = new Date(activity.start_time);
|
|
1690
|
+
const endTime = new Date(activity.end_time);
|
|
1691
|
+
const rewardItemName = ItemConfig[activity.reward_item] || "未知物品";
|
|
1692
|
+
let status = "";
|
|
1693
|
+
if (now < startTime) {
|
|
1694
|
+
status = "🔘 未开始";
|
|
1695
|
+
} else if (now > endTime) {
|
|
1696
|
+
status = "⏱️ 已结束";
|
|
1697
|
+
} else {
|
|
1698
|
+
status = "✅ 进行中";
|
|
1699
|
+
}
|
|
1700
|
+
let claimsInfo = "";
|
|
1701
|
+
if (activity.max_claims > 0) {
|
|
1702
|
+
const claimLogs = await ctx.database.get("ggcevo_activity_claim_log", { activity_id: activity.id });
|
|
1703
|
+
const claimCount = claimLogs.length;
|
|
1704
|
+
const remainingCount = activity.max_claims - claimCount;
|
|
1705
|
+
claimsInfo = `${remainingCount}/${activity.max_claims}`;
|
|
1706
|
+
}
|
|
1707
|
+
const requiredGroupText = activity.required_group && activity.required_group !== "" ? activity.required_group : "无限制";
|
|
1708
|
+
message += `【${activity.id}】${activity.name} ${status}
|
|
1709
|
+
`;
|
|
1710
|
+
message += ` 描述:${activity.description}
|
|
1711
|
+
`;
|
|
1712
|
+
message += ` 奖励:${rewardItemName} x${activity.reward_amount}
|
|
1713
|
+
`;
|
|
1714
|
+
message += ` 时间:${startTime.toLocaleDateString("zh-CN")} ~ ${endTime.toLocaleDateString("zh-CN")}
|
|
1715
|
+
`;
|
|
1716
|
+
if (claimsInfo) {
|
|
1717
|
+
message += ` 剩余:${claimsInfo}
|
|
1718
|
+
`;
|
|
1719
|
+
}
|
|
1720
|
+
message += ` 限制群聊:${requiredGroupText}
|
|
1721
|
+
`;
|
|
1722
|
+
message += `─────────────
|
|
1723
|
+
`;
|
|
1724
|
+
}
|
|
1725
|
+
return message;
|
|
1726
|
+
});
|
|
1727
|
+
ctx.command("ggcevo/补签").action(async (argv) => {
|
|
1728
|
+
const session = argv.session;
|
|
1729
|
+
const handle = await getHandle(session);
|
|
1730
|
+
if (!handle) {
|
|
1731
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
1732
|
+
}
|
|
1733
|
+
const now = /* @__PURE__ */ new Date();
|
|
1734
|
+
const currentYear = now.getFullYear();
|
|
1735
|
+
const currentMonth = now.getMonth();
|
|
1736
|
+
const currentMonthNum = currentYear * 100 + (currentMonth + 1);
|
|
1737
|
+
const [makeupCoupon] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 9 });
|
|
1738
|
+
const couponCount = makeupCoupon?.count || 0;
|
|
1739
|
+
if (couponCount <= 0) {
|
|
1740
|
+
return "❌ 补签券不足!需要1张补签券进行补签。";
|
|
1741
|
+
}
|
|
1742
|
+
const startOfMonth = new Date(currentYear, currentMonth, 1);
|
|
1743
|
+
const endOfMonth = new Date(currentYear, currentMonth + 1, 0, 23, 59, 59, 999);
|
|
1744
|
+
const existingLogs = await ctx.database.get("ggcevo_signin_log", {
|
|
1745
|
+
user_id: handle,
|
|
1746
|
+
signin_date: { $gte: startOfMonth, $lte: endOfMonth }
|
|
1747
|
+
});
|
|
1748
|
+
const signedDates = /* @__PURE__ */ new Set();
|
|
1749
|
+
for (const log of existingLogs) {
|
|
1750
|
+
const date = new Date(log.signin_date);
|
|
1751
|
+
const dateStr2 = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
1752
|
+
signedDates.add(dateStr2);
|
|
1753
|
+
}
|
|
1754
|
+
const today = new Date(currentYear, currentMonth, now.getDate());
|
|
1755
|
+
const missedDates = [];
|
|
1756
|
+
for (let d = new Date(startOfMonth); d < today; d.setDate(d.getDate() + 1)) {
|
|
1757
|
+
const dateStr2 = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
1758
|
+
if (!signedDates.has(dateStr2)) {
|
|
1759
|
+
missedDates.push(new Date(d));
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
if (missedDates.length === 0) {
|
|
1763
|
+
return "📅 本月没有可补签的日期!";
|
|
1764
|
+
}
|
|
1765
|
+
const earliestMissedDate = missedDates[0];
|
|
1766
|
+
const [summary] = await ctx.database.get("ggcevo_signin_summary", { user_id: handle });
|
|
1767
|
+
let currentMonthDays = 0;
|
|
1768
|
+
if (summary && summary.current_month === currentMonthNum) {
|
|
1769
|
+
currentMonthDays = summary.month_days;
|
|
1770
|
+
}
|
|
1771
|
+
const newMonthDaysAfterMakeup = currentMonthDays + 1;
|
|
1772
|
+
const monthRewardConfig = {
|
|
1773
|
+
7: 1,
|
|
1774
|
+
14: 2,
|
|
1775
|
+
21: 3,
|
|
1776
|
+
28: 4
|
|
1777
|
+
};
|
|
1778
|
+
let gugubReward = 3;
|
|
1779
|
+
let extraGugubReward = 0;
|
|
1780
|
+
if (monthRewardConfig[newMonthDaysAfterMakeup]) {
|
|
1781
|
+
extraGugubReward = monthRewardConfig[newMonthDaysAfterMakeup];
|
|
1782
|
+
gugubReward += extraGugubReward;
|
|
1783
|
+
}
|
|
1784
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
1785
|
+
user_id: handle,
|
|
1786
|
+
item_id: 9,
|
|
1787
|
+
count: couponCount - 1
|
|
1788
|
+
}]);
|
|
1789
|
+
await ctx.database.create("ggcevo_signin_log", {
|
|
1790
|
+
user_id: handle,
|
|
1791
|
+
signin_date: earliestMissedDate,
|
|
1792
|
+
signin_type: 1,
|
|
1793
|
+
reward_config: "makeup",
|
|
1794
|
+
create_time: now
|
|
1795
|
+
});
|
|
1796
|
+
const [gugubItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: 2 });
|
|
1797
|
+
const newGugubCount = (gugubItem?.count || 0) + gugubReward;
|
|
1798
|
+
if (gugubItem) {
|
|
1799
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
1800
|
+
user_id: handle,
|
|
1801
|
+
item_id: 2,
|
|
1802
|
+
count: newGugubCount
|
|
1803
|
+
}]);
|
|
1804
|
+
} else {
|
|
1805
|
+
await ctx.database.create("ggcevo_backpack", {
|
|
1806
|
+
user_id: handle,
|
|
1807
|
+
item_id: 2,
|
|
1808
|
+
count: newGugubCount
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
let newMonthDays = newMonthDaysAfterMakeup;
|
|
1812
|
+
let newTotalDays = 1;
|
|
1813
|
+
if (summary) {
|
|
1814
|
+
newTotalDays = summary.total_days + 1;
|
|
1815
|
+
}
|
|
1816
|
+
await ctx.database.upsert("ggcevo_signin_summary", [{
|
|
1817
|
+
user_id: handle,
|
|
1818
|
+
total_days: newTotalDays,
|
|
1819
|
+
month_days: newMonthDays,
|
|
1820
|
+
current_month: currentMonthNum,
|
|
1821
|
+
continuous_days: summary?.continuous_days || 0,
|
|
1822
|
+
last_signin_date: summary?.last_signin_date || now,
|
|
1823
|
+
update_time: now,
|
|
1824
|
+
last_allowance_month: summary?.last_allowance_month || 0
|
|
1825
|
+
}]);
|
|
1826
|
+
const dateStr = earliestMissedDate.toLocaleDateString("zh-CN");
|
|
1827
|
+
let message = `🎁 补签成功!
|
|
1828
|
+
补签日期:${dateStr}
|
|
1829
|
+
消耗:1 补签券
|
|
1830
|
+
获得:3 咕咕币
|
|
1831
|
+
本月签到:${newMonthDays} 天
|
|
1832
|
+
累计签到:${newTotalDays} 天`;
|
|
1833
|
+
if (extraGugubReward > 0) {
|
|
1834
|
+
message += `
|
|
1835
|
+
⭐ 本月第${newMonthDays}次签到额外奖励:${extraGugubReward} 咕咕币`;
|
|
1836
|
+
}
|
|
1837
|
+
return message;
|
|
1838
|
+
});
|
|
1839
|
+
ctx.command("ggcevo/使用 <name:string>").action(async (argv, name2) => {
|
|
1840
|
+
const session = argv.session;
|
|
1841
|
+
const handle = await getHandle(session);
|
|
1842
|
+
if (!handle) {
|
|
1843
|
+
return "🔒 需要先绑定游戏句柄。\n💡 使用 `绑定句柄` 命令进行绑定。";
|
|
1844
|
+
}
|
|
1845
|
+
if (!name2 || name2 !== "赎罪券") {
|
|
1846
|
+
return "❌ 仅支持使用赎罪券!\n💡 输入 `使用 赎罪券` 来消耗此道具。";
|
|
1847
|
+
}
|
|
1848
|
+
const itemId = 23;
|
|
1849
|
+
const [backpackItem] = await ctx.database.get("ggcevo_backpack", { user_id: handle, item_id: itemId });
|
|
1850
|
+
const itemCount = backpackItem?.count || 0;
|
|
1851
|
+
if (itemCount <= 0) {
|
|
1852
|
+
return "❌ 你没有赎罪券!请先通过兑换获取。";
|
|
1853
|
+
}
|
|
1854
|
+
await session.send('⚠️ 使用赎罪券后,请在 2 小时内联系活动管理员,否则视为作废使用!\n💬 输入 "确认" 继续使用,输入其他内容取消。');
|
|
1855
|
+
const confirm = await session.prompt(12e4);
|
|
1856
|
+
if (!confirm || confirm.trim() !== "确认") {
|
|
1857
|
+
return "❌ 已取消使用赎罪券。";
|
|
1858
|
+
}
|
|
1859
|
+
const now = /* @__PURE__ */ new Date();
|
|
1860
|
+
const newCount = itemCount - 1;
|
|
1861
|
+
if (newCount <= 0) {
|
|
1862
|
+
await ctx.database.remove("ggcevo_backpack", { user_id: handle, item_id: itemId });
|
|
1863
|
+
} else {
|
|
1864
|
+
await ctx.database.upsert("ggcevo_backpack", [{
|
|
1865
|
+
user_id: handle,
|
|
1866
|
+
item_id: itemId,
|
|
1867
|
+
count: newCount
|
|
1868
|
+
}]);
|
|
1869
|
+
}
|
|
1870
|
+
await ctx.database.create("ggcevo_exchange_log", {
|
|
1871
|
+
user_id: handle,
|
|
1872
|
+
exchange_id: itemId,
|
|
1873
|
+
cost_type: 4,
|
|
1874
|
+
create_time: now
|
|
1875
|
+
});
|
|
1876
|
+
return `✅ 赎罪券使用成功!`;
|
|
1877
|
+
});
|
|
1878
|
+
}
|
|
1879
|
+
__name(apply, "apply");
|
|
1880
|
+
function toBeijingTime(isoString) {
|
|
1881
|
+
const date = new Date(isoString);
|
|
1882
|
+
const beijingTime = new Date(date.getTime() + 8 * 60 * 60 * 1e3);
|
|
1883
|
+
const pad = /* @__PURE__ */ __name((n) => String(n).padStart(2, "0"), "pad");
|
|
1884
|
+
const y = beijingTime.getUTCFullYear();
|
|
1885
|
+
const M = pad(beijingTime.getUTCMonth() + 1);
|
|
1886
|
+
const d = pad(beijingTime.getUTCDate());
|
|
1887
|
+
const h2 = pad(beijingTime.getUTCHours());
|
|
1888
|
+
const m = pad(beijingTime.getUTCMinutes());
|
|
1889
|
+
const s = pad(beijingTime.getUTCSeconds());
|
|
1890
|
+
return `${y}/${M}/${d} ${h2}:${m}:${s}`;
|
|
1891
|
+
}
|
|
1892
|
+
__name(toBeijingTime, "toBeijingTime");
|
|
1893
|
+
function translateEventType(eventType) {
|
|
1894
|
+
const translations = {
|
|
1895
|
+
cameBackOnline: "恢复在线",
|
|
1896
|
+
wentOffline: "离线"
|
|
1897
|
+
};
|
|
1898
|
+
return translations[eventType] || eventType;
|
|
1899
|
+
}
|
|
1900
|
+
__name(translateEventType, "translateEventType");
|
|
1901
|
+
function formatRecentEvents(events) {
|
|
1902
|
+
if (!events || events.length === 0) return "无";
|
|
1903
|
+
const latest5 = events.slice(0, 5);
|
|
1904
|
+
return latest5.map(
|
|
1905
|
+
(e) => `${translateEventType(e.eventType)} (${toBeijingTime(e.eventTime)})`
|
|
1906
|
+
).join(", ");
|
|
1907
|
+
}
|
|
1908
|
+
__name(formatRecentEvents, "formatRecentEvents");
|
|
1909
|
+
async function makeHttpRequest(ctx, url) {
|
|
1910
|
+
return await ctx.http.get(url);
|
|
1911
|
+
}
|
|
1912
|
+
__name(makeHttpRequest, "makeHttpRequest");
|
|
1913
|
+
function formatMapMonitorMessage(currentData, previousRecord) {
|
|
1914
|
+
const previousData = JSON.parse(previousRecord.lastState);
|
|
1915
|
+
const lines = [];
|
|
1916
|
+
lines.push("🔔 地图状态变更通知");
|
|
1917
|
+
if (currentData.mapName) {
|
|
1918
|
+
lines.push(`地图: ${currentData.mapName} (ID: ${currentData.mapId})`);
|
|
1919
|
+
} else {
|
|
1920
|
+
lines.push(`地图ID: ${currentData.mapId}`);
|
|
1921
|
+
}
|
|
1922
|
+
const fieldLabels = {
|
|
1923
|
+
isOnline: "在线状态",
|
|
1924
|
+
lastStatusChangeTime: "最后状态变更时间",
|
|
1925
|
+
offlineCountLast24h: "24h内离线次数",
|
|
1926
|
+
offlineCountLast30d: "30d内离线次数"
|
|
1927
|
+
};
|
|
1928
|
+
const newValueOnlyFields = ["lastStatusChangeTime", "offlineCountLast24h", "offlineCountLast30d"];
|
|
1929
|
+
const timeFields = ["lastCheckTime", "lastStatusChangeTime", "firstSeenTime"];
|
|
1930
|
+
const formatValue = /* @__PURE__ */ __name((key, raw) => {
|
|
1931
|
+
if (key === "isOnline") {
|
|
1932
|
+
try {
|
|
1933
|
+
const parsed = JSON.parse(raw);
|
|
1934
|
+
return typeof parsed === "boolean" ? parsed ? "🟢 在线" : "🔴 离线" : raw;
|
|
1935
|
+
} catch {
|
|
1936
|
+
return raw;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
if (key === "recentEvents") {
|
|
1940
|
+
try {
|
|
1941
|
+
const events = JSON.parse(raw);
|
|
1942
|
+
return formatRecentEvents(events);
|
|
1943
|
+
} catch {
|
|
1944
|
+
return raw;
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
if (timeFields.includes(key)) {
|
|
1948
|
+
try {
|
|
1949
|
+
return toBeijingTime(JSON.parse(raw));
|
|
1950
|
+
} catch {
|
|
1951
|
+
return raw;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
return raw;
|
|
1955
|
+
}, "formatValue");
|
|
1956
|
+
for (const key of Object.keys(currentData)) {
|
|
1957
|
+
if (key === "mapId" || key === "mapName" || key === "recentEvents" || key === "popularityRank" || key === "lastCheckTime") continue;
|
|
1958
|
+
const prevValue = JSON.stringify(previousData[key]);
|
|
1959
|
+
const currValue = JSON.stringify(currentData[key]);
|
|
1960
|
+
if (prevValue !== currValue) {
|
|
1961
|
+
const label = fieldLabels[key] || key;
|
|
1962
|
+
if (newValueOnlyFields.includes(key)) {
|
|
1963
|
+
lines.push(`${label}: ${formatValue(key, currValue)}`);
|
|
1964
|
+
} else {
|
|
1965
|
+
lines.push(`${label}: ${formatValue(key, prevValue)} → ${formatValue(key, currValue)}`);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return lines.join("\n");
|
|
1970
|
+
}
|
|
1971
|
+
__name(formatMapMonitorMessage, "formatMapMonitorMessage");
|
|
1972
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1973
|
+
0 && (module.exports = {
|
|
1974
|
+
Config,
|
|
1975
|
+
ExchangeConfig,
|
|
1976
|
+
ItemConfig,
|
|
1977
|
+
LotteryPoolConfig,
|
|
1978
|
+
apply,
|
|
1979
|
+
inject,
|
|
1980
|
+
name
|
|
1981
|
+
});
|