koishi-plugin-msbao 0.0.16 → 0.0.18
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/commands/keyword.d.ts +3 -0
- package/lib/commands/maple.d.ts +5 -0
- package/lib/commands/web.d.ts +3 -0
- package/lib/config.d.ts +35 -0
- package/lib/index.d.ts +3 -35
- package/lib/index.js +226 -150
- package/lib/utils/bindings.d.ts +11 -0
- package/lib/utils/whitelist.d.ts +6 -0
- package/package.json +32 -31
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { MapleStoryApi } from 'maplestory-openapi/tms';
|
|
3
|
+
import { Config } from '../config';
|
|
4
|
+
import { BindingsManager } from '../utils/bindings';
|
|
5
|
+
export declare function applyMapleCommands(ctx: Context, config: Config, api: MapleStoryApi, bindingsMgr: BindingsManager): void;
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Schema } from 'koishi';
|
|
2
|
+
export interface Config {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
whitelistMode: boolean;
|
|
5
|
+
whitelist: string[];
|
|
6
|
+
admins: string[];
|
|
7
|
+
apiKey: string;
|
|
8
|
+
ms: {
|
|
9
|
+
useGlobalwlist: boolean;
|
|
10
|
+
selfWhitelist: string[];
|
|
11
|
+
queryInterval?: number;
|
|
12
|
+
images?: string[];
|
|
13
|
+
dataPath?: string;
|
|
14
|
+
};
|
|
15
|
+
URL: {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
Lists: Array<{
|
|
18
|
+
name: string;
|
|
19
|
+
websites: string[];
|
|
20
|
+
useGlobalwlist: boolean;
|
|
21
|
+
selfWhitelist: string[];
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
Key: {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
keywords: Array<{
|
|
27
|
+
listening: string;
|
|
28
|
+
reply: string;
|
|
29
|
+
useGlobalwlist: boolean;
|
|
30
|
+
selfWhitelist: string[];
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare const Config: Schema<Config>;
|
|
35
|
+
export declare const name = "msbao";
|
package/lib/index.d.ts
CHANGED
|
@@ -1,36 +1,4 @@
|
|
|
1
|
-
import { Context
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
whitelistMode: boolean;
|
|
5
|
-
whitelist: string[];
|
|
6
|
-
admins: string[];
|
|
7
|
-
apiKey: string;
|
|
8
|
-
ms: {
|
|
9
|
-
useGlobalwlist: boolean;
|
|
10
|
-
selfWhitelist: string[];
|
|
11
|
-
queryInterval?: number;
|
|
12
|
-
images?: string[];
|
|
13
|
-
dataPath?: string;
|
|
14
|
-
};
|
|
15
|
-
URL: {
|
|
16
|
-
enabled: boolean;
|
|
17
|
-
Lists: Array<{
|
|
18
|
-
name: string;
|
|
19
|
-
websites: string[];
|
|
20
|
-
useGlobalwlist: boolean;
|
|
21
|
-
selfWhitelist: string[];
|
|
22
|
-
}>;
|
|
23
|
-
};
|
|
24
|
-
Key: {
|
|
25
|
-
enabled: boolean;
|
|
26
|
-
keywords: Array<{
|
|
27
|
-
listening: string;
|
|
28
|
-
reply: string;
|
|
29
|
-
useGlobalwlist: boolean;
|
|
30
|
-
selfWhitelist: string[];
|
|
31
|
-
}>;
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
export declare const Config: Schema<Config>;
|
|
35
|
-
export declare const name = "msbao";
|
|
1
|
+
import { Context } from 'koishi';
|
|
2
|
+
import { Config, name } from './config';
|
|
3
|
+
export { name, Config };
|
|
36
4
|
export declare function apply(ctx: Context, config: Config): void;
|
package/lib/index.js
CHANGED
|
@@ -69,6 +69,107 @@ var Config = import_koishi.Schema.object({
|
|
|
69
69
|
})
|
|
70
70
|
});
|
|
71
71
|
var name = "msbao";
|
|
72
|
+
function formatDateToYMD(dateInput) {
|
|
73
|
+
if (!dateInput) return "未知";
|
|
74
|
+
let date;
|
|
75
|
+
if (typeof dateInput === "string") {
|
|
76
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(dateInput)) {
|
|
77
|
+
return dateInput;
|
|
78
|
+
}
|
|
79
|
+
date = new Date(dateInput);
|
|
80
|
+
} else {
|
|
81
|
+
date = dateInput;
|
|
82
|
+
}
|
|
83
|
+
if (isNaN(date.getTime())) {
|
|
84
|
+
return "无效日期";
|
|
85
|
+
}
|
|
86
|
+
const year = date.getFullYear();
|
|
87
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
88
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
89
|
+
return `${year}-${month}-${day}`;
|
|
90
|
+
}
|
|
91
|
+
__name(formatDateToYMD, "formatDateToYMD");
|
|
92
|
+
function formatNumber(numStr) {
|
|
93
|
+
let num = parseInt(numStr, 10);
|
|
94
|
+
if (isNaN(num) || num < 0) return numStr;
|
|
95
|
+
let parts = [];
|
|
96
|
+
if (num >= 1e8) {
|
|
97
|
+
const yi = Math.floor(num / 1e8);
|
|
98
|
+
parts.push(`${yi}亿`);
|
|
99
|
+
num %= 1e8;
|
|
100
|
+
}
|
|
101
|
+
if (num >= 1e4) {
|
|
102
|
+
const wan = Math.floor(num / 1e4);
|
|
103
|
+
parts.push(`${wan}万`);
|
|
104
|
+
num %= 1e4;
|
|
105
|
+
}
|
|
106
|
+
if (num > 0) {
|
|
107
|
+
parts.push(num.toString());
|
|
108
|
+
}
|
|
109
|
+
return parts.length ? parts.join("") : "0";
|
|
110
|
+
}
|
|
111
|
+
__name(formatNumber, "formatNumber");
|
|
112
|
+
function buildStatMap(finalStat) {
|
|
113
|
+
const map = {};
|
|
114
|
+
for (const item of finalStat) {
|
|
115
|
+
map[item.statName] = item.statValue;
|
|
116
|
+
}
|
|
117
|
+
return map;
|
|
118
|
+
}
|
|
119
|
+
__name(buildStatMap, "buildStatMap");
|
|
120
|
+
async function formatCharacterInfo(api, ocid, characterName) {
|
|
121
|
+
const basic = await api.getCharacterBasic(ocid);
|
|
122
|
+
const stat = await api.getCharacterStat(ocid);
|
|
123
|
+
if (!basic || !stat?.finalStat) {
|
|
124
|
+
throw new Error("无法获取完整角色数据");
|
|
125
|
+
}
|
|
126
|
+
const stats = buildStatMap(stat.finalStat);
|
|
127
|
+
const createDate = formatDateToYMD(basic.characterDateCreate);
|
|
128
|
+
const encoded = encodeURIComponent(characterName);
|
|
129
|
+
const statLines = [
|
|
130
|
+
`战斗力: ${formatNumber(stats["戰鬥力"] || "0")}`,
|
|
131
|
+
//🌟
|
|
132
|
+
`物攻 / 魔攻: ${stats["攻擊力"] || "0"} / ${stats["魔法攻擊力"] || "0"}`,
|
|
133
|
+
//⚔️
|
|
134
|
+
// `魔攻: ${stats['魔法攻擊力'] || '0'}`, //🔮
|
|
135
|
+
`最终伤害: ${stats["最終傷害"] || "0"}%`,
|
|
136
|
+
//🎯
|
|
137
|
+
`暴击伤害: ${stats["爆擊傷害"] || "0"}%`,
|
|
138
|
+
//🧨
|
|
139
|
+
`BOSS伤害: ${stats["BOSS怪物傷害"] || "0"}%`,
|
|
140
|
+
//👹
|
|
141
|
+
`一般伤害: ${stats["一般怪物傷害"] || "0"}%`,
|
|
142
|
+
//👾
|
|
143
|
+
`伤害: ${stats["傷害"] || "0"}%`,
|
|
144
|
+
//💥
|
|
145
|
+
`无视防御: ${stats["無視防禦率"] || "0"}%`,
|
|
146
|
+
//🛡️
|
|
147
|
+
`星力: ${stats["星力"] || "0"}`,
|
|
148
|
+
//⭐
|
|
149
|
+
`神秘力量(ARC): ${stats["神秘力量"] || "0"}`,
|
|
150
|
+
//🌀
|
|
151
|
+
`真实力量(AUT): ${stats["真實之力"] || "0"}`,
|
|
152
|
+
//✨
|
|
153
|
+
`道具掉落率: ${stats["道具掉落率"] || "0"}%`,
|
|
154
|
+
//📦
|
|
155
|
+
`枫币获得量: ${stats["楓幣獲得量"] || "0"}%`,
|
|
156
|
+
//💰
|
|
157
|
+
`冷卻减少(秒): ${stats["冷卻時間減少(秒)"] || "0"}秒`
|
|
158
|
+
//⏱️
|
|
159
|
+
].join("\n");
|
|
160
|
+
return `${basic.characterName} (${basic.worldName}@${basic.characterGuildName || "无公会"})
|
|
161
|
+
${basic.characterClass} | Lv.${basic.characterLevel} (${basic.characterExpRate}%)
|
|
162
|
+
建立日期: ${createDate}
|
|
163
|
+
|
|
164
|
+
详细属性:
|
|
165
|
+
${statLines}
|
|
166
|
+
更多详细信息:
|
|
167
|
+
https://maplescouter.com/info?name=${encoded}
|
|
168
|
+
|
|
169
|
+
OCID(角色ID): ${ocid}
|
|
170
|
+
(↑即使改名,OCID也不会变,可通过%ocid ocid 查询当前信息)`;
|
|
171
|
+
}
|
|
172
|
+
__name(formatCharacterInfo, "formatCharacterInfo");
|
|
72
173
|
function apply(ctx, config) {
|
|
73
174
|
if (!config.enabled) return;
|
|
74
175
|
const isAdmin = /* @__PURE__ */ __name((session) => config.admins.includes(session.userId), "isAdmin");
|
|
@@ -168,22 +269,32 @@ ${i.websites.join("\n")}
|
|
|
168
269
|
const api = new import_tms.MapleStoryApi(config.apiKey);
|
|
169
270
|
ctx.command("%查询 <name:string>", "查询TMS角色信息").alias("%查詢").action(async ({ session }, name2) => {
|
|
170
271
|
if (!canUse(session, config.ms)) return "";
|
|
171
|
-
if (!name2) return "请提供角色名, 用法: %查询 角色名
|
|
272
|
+
if (!name2) return "请提供角色名, 用法: %查询 角色名";
|
|
172
273
|
try {
|
|
173
274
|
const character = await api.getCharacter(name2);
|
|
174
275
|
const ocid = character.ocid;
|
|
175
276
|
if (!ocid) return "查询失败,请检查角色名";
|
|
277
|
+
return await formatCharacterInfo(api, ocid, name2);
|
|
278
|
+
} catch (err) {
|
|
279
|
+
if (err.constructor.name === "MapleStoryApiError") {
|
|
280
|
+
return `查询失败,请检查角色名`;
|
|
281
|
+
}
|
|
282
|
+
return `查询失败,请稍后再试或联系开发者(布丁@2482457432 )`;
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
ctx.command("%ocid <ocid:string>", "通过OCID(角色ID)直接查询TMS角色信息").alias("%OCID").action(async ({ session }, ocid) => {
|
|
286
|
+
if (!canUse(session, config.ms)) return "";
|
|
287
|
+
if (!ocid) return "需提供OCID,用法: %ocid ocid";
|
|
288
|
+
if (!/^[0-9a-f]{32}$/.test(ocid)) {
|
|
289
|
+
return "OCID格式不正确(应为字母+数字的组合)";
|
|
290
|
+
}
|
|
291
|
+
try {
|
|
176
292
|
const basic = await api.getCharacterBasic(ocid);
|
|
177
|
-
if (!basic) return "
|
|
178
|
-
|
|
179
|
-
return `${basic.characterName} (${basic.worldName}@${basic.characterGuildName || "无公会"})
|
|
180
|
-
${basic.characterClass} | Lv.${basic.characterLevel} (${basic.characterExpRate + "%"})
|
|
181
|
-
|
|
182
|
-
详细信息:
|
|
183
|
-
https://maplescouter.com/info?name=${encoded}`;
|
|
293
|
+
if (!basic?.characterName) return "查询失败,请检查OCID";
|
|
294
|
+
return await formatCharacterInfo(api, ocid, basic.characterName);
|
|
184
295
|
} catch (err) {
|
|
185
296
|
if (err.constructor.name === "MapleStoryApiError") {
|
|
186
|
-
return
|
|
297
|
+
return `查询失败,请检查OCID`;
|
|
187
298
|
}
|
|
188
299
|
return `查询失败,请稍后再试或联系开发者(布丁@2482457432 )`;
|
|
189
300
|
}
|
|
@@ -193,40 +304,121 @@ https://maplescouter.com/info?name=${encoded}`;
|
|
|
193
304
|
const qqId = session.userId;
|
|
194
305
|
const currentGameId = getBoundGameId(qqId);
|
|
195
306
|
if (currentGameId) {
|
|
196
|
-
return
|
|
307
|
+
return `你的QQ号${qqId} 已与ID ${currentGameId} 绑定,如需换绑,先使用"%解绑"后再次绑定。`;
|
|
197
308
|
}
|
|
198
309
|
bindQQToGameId(qqId, gameId);
|
|
199
|
-
return
|
|
310
|
+
return `已成功将你的QQ号 ${qqId} 与ID ${gameId} 绑定`;
|
|
200
311
|
});
|
|
201
312
|
ctx.command("%我的信息", "查询绑定的游戏角色信息").action(async ({ session }) => {
|
|
202
313
|
const qqId = session.userId;
|
|
203
314
|
const boundGameId = getBoundGameId(qqId);
|
|
204
315
|
if (!boundGameId) {
|
|
205
|
-
return "
|
|
316
|
+
return "你尚未绑定角色名,快使用 %绑定 角色名 指令进行绑定吧";
|
|
206
317
|
}
|
|
207
318
|
if (!canUse(session, config.ms)) return "";
|
|
208
319
|
if (!config.apiKey) {
|
|
209
|
-
return "API
|
|
320
|
+
return "API密钥未设置,请联系开发者配置API密钥";
|
|
210
321
|
}
|
|
211
322
|
try {
|
|
212
323
|
const character = await api.getCharacter(boundGameId);
|
|
213
324
|
const ocid = character.ocid;
|
|
214
325
|
if (!ocid) return "查询失败,请检查角色名";
|
|
215
|
-
|
|
216
|
-
if (!basic) return "查询失败,请检查角色名";
|
|
217
|
-
const encoded = encodeURIComponent(boundGameId);
|
|
218
|
-
return `${basic.characterName} (${basic.worldName}@${basic.characterGuildName || "无公会"})
|
|
219
|
-
${basic.characterClass} | Lv.${basic.characterLevel} (${basic.characterExpRate + "%"})
|
|
220
|
-
|
|
221
|
-
详细信息:
|
|
222
|
-
https://maplescouter.com/info?name=${encoded}`;
|
|
326
|
+
return await formatCharacterInfo(api, ocid, boundGameId);
|
|
223
327
|
} catch (err) {
|
|
224
328
|
if (err.constructor.name === "MapleStoryApiError") {
|
|
225
|
-
return
|
|
329
|
+
return `查询失败,请检查角色名`;
|
|
226
330
|
}
|
|
227
331
|
return `查询失败,请稍后再试或联系开发者(布丁@2482457432 )`;
|
|
228
332
|
}
|
|
229
333
|
});
|
|
334
|
+
async function analyzeExpTrend(api2, ocid, queryInterval) {
|
|
335
|
+
const tst = new Date(Date.now() + 8 * 36e5);
|
|
336
|
+
function getTstDate(offsetDay) {
|
|
337
|
+
const d = new Date(tst.getTime() + offsetDay * 864e5);
|
|
338
|
+
d.setHours(0, 0, 0, 0);
|
|
339
|
+
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
340
|
+
}
|
|
341
|
+
__name(getTstDate, "getTstDate");
|
|
342
|
+
const dates = [null];
|
|
343
|
+
for (let i = 1; i <= 7; i++) dates.push(getTstDate(-i));
|
|
344
|
+
const basics = [];
|
|
345
|
+
for (let i = 0; i < dates.length; i++) {
|
|
346
|
+
const date = dates[i];
|
|
347
|
+
try {
|
|
348
|
+
await new Promise((r) => setTimeout(r, queryInterval));
|
|
349
|
+
const b = date === null ? await api2.getCharacterBasic(ocid) : await api2.getCharacterBasic(ocid, date);
|
|
350
|
+
basics.push(b);
|
|
351
|
+
} catch (e) {
|
|
352
|
+
basics.push(null);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
const valid = basics.filter((b) => b);
|
|
356
|
+
if (valid.length < 2) throw new Error("网络错误(");
|
|
357
|
+
const records = [];
|
|
358
|
+
for (let i = 0; i < valid.length; i++) {
|
|
359
|
+
records.push({
|
|
360
|
+
level: valid[i].characterLevel,
|
|
361
|
+
expRate: Number(valid[i].characterExpRate),
|
|
362
|
+
label: i === 0 ? "目 前" : `${i}天前`
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
const gains = [];
|
|
366
|
+
for (let i = 0; i < records.length - 1; i++) {
|
|
367
|
+
const curr = records[i];
|
|
368
|
+
const prev = records[i + 1];
|
|
369
|
+
let gain = 0;
|
|
370
|
+
if (curr.level > prev.level) {
|
|
371
|
+
const levelDiff = curr.level - prev.level;
|
|
372
|
+
gain = 100 - prev.expRate + curr.expRate + (levelDiff - 1) * 100;
|
|
373
|
+
} else if (curr.level === prev.level) {
|
|
374
|
+
gain = curr.expRate - prev.expRate;
|
|
375
|
+
} else {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
gains.push(gain);
|
|
379
|
+
records[i].gain = gain;
|
|
380
|
+
}
|
|
381
|
+
const start = records[records.length - 1];
|
|
382
|
+
const end = records[0];
|
|
383
|
+
let totalGrowthStr = "";
|
|
384
|
+
if (end.level > start.level) {
|
|
385
|
+
totalGrowthStr = `+${end.level - start.level} Lv`;
|
|
386
|
+
} else {
|
|
387
|
+
const totalGain = end.expRate - start.expRate;
|
|
388
|
+
totalGrowthStr = `${totalGain >= 0 ? "+" : ""}${totalGain.toFixed(3)}%`;
|
|
389
|
+
}
|
|
390
|
+
const avgGain = gains.length ? gains.reduce((a, b) => a + b, 0) / gains.length : 0;
|
|
391
|
+
const currentRate = end.expRate;
|
|
392
|
+
const gap = 100 - currentRate;
|
|
393
|
+
const predictDays = avgGain <= 0 ? "∞" : Math.max(1, Math.ceil(gap / avgGain)).toString();
|
|
394
|
+
const upgradeDate = new Date(Date.now() + parseInt(predictDays) * 864e5);
|
|
395
|
+
const upgradeStr = `${upgradeDate.getFullYear()}-${String(upgradeDate.getMonth() + 1).padStart(2, "0")}-${String(upgradeDate.getDate()).padStart(2, "0")}`;
|
|
396
|
+
let lines = `${valid[0].characterName}·${valid[0].characterClass} (${valid[0].worldName}@${valid[0].characterGuildName || "无公会"})
|
|
397
|
+
|
|
398
|
+
经验变化:
|
|
399
|
+
`;
|
|
400
|
+
for (let i = 0; i < records.length - 1; i++) {
|
|
401
|
+
const curr = records[i];
|
|
402
|
+
const gain = curr.gain;
|
|
403
|
+
if (gain !== void 0) {
|
|
404
|
+
const sign = gain >= 0 ? "+" : "";
|
|
405
|
+
lines += `${curr.label}: Lv.${curr.level} (${curr.expRate.toFixed(3)}%) [${sign}${gain.toFixed(3)}%]
|
|
406
|
+
`;
|
|
407
|
+
} else {
|
|
408
|
+
lines += `${curr.label}: Lv.${curr.level} (${curr.expRate.toFixed(3)}%)
|
|
409
|
+
`;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
lines += `------------------------------
|
|
413
|
+
近期成长: ${totalGrowthStr}
|
|
414
|
+
日均+${avgGain.toFixed(3)}% /天
|
|
415
|
+
预计升级还需: ${predictDays} 天
|
|
416
|
+
预计升级日期: ${upgradeStr}
|
|
417
|
+
|
|
418
|
+
(当日数据可能不准确,下午6点完成数据分割)`;
|
|
419
|
+
return lines.trimEnd();
|
|
420
|
+
}
|
|
421
|
+
__name(analyzeExpTrend, "analyzeExpTrend");
|
|
230
422
|
ctx.command("%我的经验", "查看绑定角色最近7天经验变化").alias("%我的經驗").action(async ({ session }) => {
|
|
231
423
|
const qqId = session.userId;
|
|
232
424
|
const boundGameId = getBoundGameId(qqId);
|
|
@@ -235,84 +427,26 @@ https://maplescouter.com/info?name=${encoded}`;
|
|
|
235
427
|
}
|
|
236
428
|
if (!canUse(session, config.ms)) return "";
|
|
237
429
|
if (!config.apiKey) {
|
|
238
|
-
return "API
|
|
430
|
+
return "API密钥未设置,请联系开发者配置API密钥";
|
|
239
431
|
}
|
|
240
432
|
try {
|
|
241
|
-
let getTstDate = function(offsetDay) {
|
|
242
|
-
const d = new Date(tst.getTime() + offsetDay * 864e5);
|
|
243
|
-
d.setHours(0, 0, 0, 0);
|
|
244
|
-
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
245
|
-
};
|
|
246
|
-
__name(getTstDate, "getTstDate");
|
|
247
433
|
const character = await api.getCharacter(boundGameId);
|
|
248
434
|
const ocid = character.ocid;
|
|
249
435
|
if (!ocid) return "查询失败,请检查角色名";
|
|
250
|
-
const
|
|
251
|
-
const dates = [null];
|
|
252
|
-
for (let i = 1; i <= 7; i++) dates.push(getTstDate(-i));
|
|
253
|
-
const basics = [];
|
|
254
|
-
for (let i = 0; i < dates.length; i++) {
|
|
255
|
-
const date = dates[i];
|
|
256
|
-
try {
|
|
257
|
-
await new Promise((r) => setTimeout(r, config.ms.queryInterval ?? 100));
|
|
258
|
-
const b = date === null ? await api.getCharacterBasic(ocid) : await api.getCharacterBasic(ocid, date);
|
|
259
|
-
basics.push(b);
|
|
260
|
-
} catch (e) {
|
|
261
|
-
basics.push(null);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
const valid = basics.filter((b) => b);
|
|
265
|
-
if (valid.length < 2) return "网络错误(";
|
|
266
|
-
const dailyDiffs = [];
|
|
267
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
268
|
-
const curr = valid[i];
|
|
269
|
-
const prev = valid[i + 1];
|
|
270
|
-
if (curr.characterLevel === prev.characterLevel) {
|
|
271
|
-
dailyDiffs.push(Number(curr.characterExpRate) - Number(prev.characterExpRate));
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
const avgDiff = dailyDiffs.length ? dailyDiffs.reduce((a, b) => a + b, 0) / dailyDiffs.length : 0;
|
|
275
|
-
const currentRate = Number(valid[0].characterExpRate);
|
|
276
|
-
const gap = 100 - currentRate;
|
|
277
|
-
const predictDays = avgDiff <= 0 ? "∞" : Math.max(1, Math.ceil(gap / avgDiff)).toString();
|
|
278
|
-
const upgradeDate = new Date(Date.now() + parseInt(predictDays) * 864e5);
|
|
279
|
-
const upgradeStr = `${upgradeDate.getFullYear()}-${String(upgradeDate.getMonth() + 1).padStart(2, "0")}-${String(upgradeDate.getDate()).padStart(2, "0")}`;
|
|
280
|
-
const head = valid[0];
|
|
281
|
-
let lines = `${head.characterName}·${head.characterClass} (${head.worldName}@${head.characterGuildName || "无公会"})
|
|
282
|
-
经验变化:
|
|
283
|
-
`;
|
|
284
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
285
|
-
const curr = valid[i];
|
|
286
|
-
const prev = valid[i + 1];
|
|
287
|
-
if (curr.characterLevel > prev.characterLevel) {
|
|
288
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)
|
|
289
|
-
`;
|
|
290
|
-
} else {
|
|
291
|
-
const diff = (Number(curr.characterExpRate) - Number(prev.characterExpRate)).toFixed(3);
|
|
292
|
-
const sign = diff.startsWith("-") ? "" : "+";
|
|
293
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)[${sign}${diff}%]
|
|
294
|
-
`;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
lines += `----------------------
|
|
298
|
-
日均+${avgDiff.toFixed(3)}%/天
|
|
299
|
-
预计升级还需: ${predictDays} 天
|
|
300
|
-
预计升级日期: ${upgradeStr}
|
|
301
|
-
|
|
302
|
-
(如若升级则不计算日均增长,可能出现预计数据报错)
|
|
303
|
-
(当日数据可能不准确,下午6点完成更新)`;
|
|
436
|
+
const resultText = await analyzeExpTrend(api, ocid, config.ms.queryInterval ?? 100);
|
|
304
437
|
const candidates = config.ms.images?.map((s) => s.trim()).filter(Boolean) || [];
|
|
305
438
|
const existFiles = candidates.map((f) => (0, import_path.resolve)(process.cwd(), f)).filter((f) => (0, import_fs.existsSync)(f));
|
|
306
439
|
if (existFiles.length) {
|
|
307
440
|
const picked = existFiles[Math.floor(Math.random() * existFiles.length)];
|
|
308
441
|
return [
|
|
309
|
-
|
|
442
|
+
resultText,
|
|
310
443
|
import_koishi.h.image((0, import_url.pathToFileURL)(picked).href)
|
|
311
444
|
];
|
|
312
445
|
}
|
|
313
|
-
return
|
|
446
|
+
return resultText;
|
|
314
447
|
} catch (err) {
|
|
315
|
-
if (err.
|
|
448
|
+
if (err.message === "网络错误(") return "网络错误(";
|
|
449
|
+
if (err.constructor.name === "MapleStoryApiError") return "查询失败,请检查角色名";
|
|
316
450
|
return "查询失败,请稍后再试或联系开发者(布丁@2482457432 )";
|
|
317
451
|
}
|
|
318
452
|
});
|
|
@@ -320,84 +454,26 @@ https://maplescouter.com/info?name=${encoded}`;
|
|
|
320
454
|
if (!canUse(session, config.ms)) return "";
|
|
321
455
|
if (!name2) return "请提供角色名";
|
|
322
456
|
if (!config.apiKey) {
|
|
323
|
-
return "API
|
|
457
|
+
return "API密钥未设置,请联系开发者配置API密钥";
|
|
324
458
|
}
|
|
325
459
|
try {
|
|
326
|
-
let getTstDate = function(offsetDay) {
|
|
327
|
-
const d = new Date(tst.getTime() + offsetDay * 864e5);
|
|
328
|
-
d.setHours(0, 0, 0, 0);
|
|
329
|
-
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
330
|
-
};
|
|
331
|
-
__name(getTstDate, "getTstDate");
|
|
332
460
|
const character = await api.getCharacter(name2);
|
|
333
461
|
const ocid = character.ocid;
|
|
334
462
|
if (!ocid) return "查询失败,请检查角色名";
|
|
335
|
-
const
|
|
336
|
-
const dates = [null];
|
|
337
|
-
for (let i = 1; i <= 7; i++) dates.push(getTstDate(-i));
|
|
338
|
-
const basics = [];
|
|
339
|
-
for (let i = 0; i < dates.length; i++) {
|
|
340
|
-
const date = dates[i];
|
|
341
|
-
try {
|
|
342
|
-
await new Promise((r) => setTimeout(r, config.ms.queryInterval ?? 100));
|
|
343
|
-
const b = date === null ? await api.getCharacterBasic(ocid) : await api.getCharacterBasic(ocid, date);
|
|
344
|
-
basics.push(b);
|
|
345
|
-
} catch (e) {
|
|
346
|
-
basics.push(null);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
const valid = basics.filter((b) => b);
|
|
350
|
-
if (valid.length < 2) return "网络错误(";
|
|
351
|
-
const dailyDiffs = [];
|
|
352
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
353
|
-
const curr = valid[i];
|
|
354
|
-
const prev = valid[i + 1];
|
|
355
|
-
if (curr.characterLevel === prev.characterLevel) {
|
|
356
|
-
dailyDiffs.push(Number(curr.characterExpRate) - Number(prev.characterExpRate));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
const avgDiff = dailyDiffs.length ? dailyDiffs.reduce((a, b) => a + b, 0) / dailyDiffs.length : 0;
|
|
360
|
-
const currentRate = Number(valid[0].characterExpRate);
|
|
361
|
-
const gap = 100 - currentRate;
|
|
362
|
-
const predictDays = avgDiff <= 0 ? "∞" : Math.max(1, Math.ceil(gap / avgDiff)).toString();
|
|
363
|
-
const upgradeDate = new Date(Date.now() + parseInt(predictDays) * 864e5);
|
|
364
|
-
const upgradeStr = `${upgradeDate.getFullYear()}-${String(upgradeDate.getMonth() + 1).padStart(2, "0")}-${String(upgradeDate.getDate()).padStart(2, "0")}`;
|
|
365
|
-
const head = valid[0];
|
|
366
|
-
let lines = `${head.characterName}·${head.characterClass} (${head.worldName}@${head.characterGuildName || "无公会"})
|
|
367
|
-
经验变化:
|
|
368
|
-
`;
|
|
369
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
370
|
-
const curr = valid[i];
|
|
371
|
-
const prev = valid[i + 1];
|
|
372
|
-
if (curr.characterLevel > prev.characterLevel) {
|
|
373
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)
|
|
374
|
-
`;
|
|
375
|
-
} else {
|
|
376
|
-
const diff = (Number(curr.characterExpRate) - Number(prev.characterExpRate)).toFixed(3);
|
|
377
|
-
const sign = diff.startsWith("-") ? "" : "+";
|
|
378
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)[${sign}${diff}%]
|
|
379
|
-
`;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
lines += `----------------------
|
|
383
|
-
日均+${avgDiff.toFixed(3)}%/天
|
|
384
|
-
预计升级还需: ${predictDays} 天
|
|
385
|
-
预计升级日期: ${upgradeStr}
|
|
386
|
-
|
|
387
|
-
(如若升级则不计算日均增长,可能出现预计数据报错)
|
|
388
|
-
(当日数据可能不准确,下午6点完成更新)`;
|
|
463
|
+
const resultText = await analyzeExpTrend(api, ocid, config.ms.queryInterval ?? 100);
|
|
389
464
|
const candidates = config.ms.images?.map((s) => s.trim()).filter(Boolean) || [];
|
|
390
465
|
const existFiles = candidates.map((f) => (0, import_path.resolve)(process.cwd(), f)).filter((f) => (0, import_fs.existsSync)(f));
|
|
391
466
|
if (existFiles.length) {
|
|
392
467
|
const picked = existFiles[Math.floor(Math.random() * existFiles.length)];
|
|
393
468
|
return [
|
|
394
|
-
|
|
469
|
+
resultText,
|
|
395
470
|
import_koishi.h.image((0, import_url.pathToFileURL)(picked).href)
|
|
396
471
|
];
|
|
397
472
|
}
|
|
398
|
-
return
|
|
473
|
+
return resultText;
|
|
399
474
|
} catch (err) {
|
|
400
|
-
if (err.
|
|
475
|
+
if (err.message === "网络错误(") return "网络错误(";
|
|
476
|
+
if (err.constructor.name === "MapleStoryApiError") return "查询失败,请检查角色名";
|
|
401
477
|
return "查询失败,请稍后再试或联系开发者(布丁@2482457432 )";
|
|
402
478
|
}
|
|
403
479
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class BindingsManager {
|
|
2
|
+
bindings: {
|
|
3
|
+
[qqId: string]: string;
|
|
4
|
+
};
|
|
5
|
+
private readonly filePath;
|
|
6
|
+
constructor(dataDir: string);
|
|
7
|
+
save(): void;
|
|
8
|
+
getBoundGameId(qqId: string): string | null;
|
|
9
|
+
bindQQToGameId(qqId: string, gameId: string): void;
|
|
10
|
+
unbindQQ(qqId: string): void;
|
|
11
|
+
}
|
package/package.json
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "koishi-plugin-msbao",
|
|
3
|
-
"description": "Pudding's plugin",
|
|
4
|
-
"version": "0.0.
|
|
5
|
-
"contributors": [
|
|
6
|
-
"MilkteaDoll <https://github.com/MilkteaDoll>"
|
|
7
|
-
],
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "git+https://github.com/MilkteaDoll/msbao.git"
|
|
11
|
-
},
|
|
12
|
-
"main": "lib/index.js",
|
|
13
|
-
"typings": "lib/index.d.ts",
|
|
14
|
-
"files": [
|
|
15
|
-
"lib",
|
|
16
|
-
"dist",
|
|
17
|
-
"LICENSE",
|
|
18
|
-
"*.png"
|
|
19
|
-
],
|
|
20
|
-
"license": "MIT",
|
|
21
|
-
"keywords": [
|
|
22
|
-
"chatbot",
|
|
23
|
-
"plugin"
|
|
24
|
-
],
|
|
25
|
-
"peerDependencies": {
|
|
26
|
-
"koishi": "^4.18.7"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"maplestory-openapi": "^3.4.1"
|
|
30
|
-
|
|
31
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "koishi-plugin-msbao",
|
|
3
|
+
"description": "Pudding's plugin",
|
|
4
|
+
"version": "0.0.18",
|
|
5
|
+
"contributors": [
|
|
6
|
+
"MilkteaDoll <https://github.com/MilkteaDoll>"
|
|
7
|
+
],
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/MilkteaDoll/msbao.git"
|
|
11
|
+
},
|
|
12
|
+
"main": "lib/index.js",
|
|
13
|
+
"typings": "lib/index.d.ts",
|
|
14
|
+
"files": [
|
|
15
|
+
"lib",
|
|
16
|
+
"dist",
|
|
17
|
+
"LICENSE",
|
|
18
|
+
"*.png"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"keywords": [
|
|
22
|
+
"chatbot",
|
|
23
|
+
"plugin"
|
|
24
|
+
],
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"koishi": "^4.18.7"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"maplestory-openapi": "^3.4.1",
|
|
30
|
+
"puppeteer-core": "^24.37.5"
|
|
31
|
+
}
|
|
32
|
+
}
|