koishi-plugin-msbao 0.0.15 → 0.0.17
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 +208 -166
- 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
|
@@ -63,30 +63,120 @@ var Config = import_koishi.Schema.object({
|
|
|
63
63
|
selfWhitelist: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").default([]).description("独立白名单"),
|
|
64
64
|
queryInterval: import_koishi.Schema.number().default(100).description("查询间隔(毫秒)"),
|
|
65
65
|
images: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").default(["image.png"]).description("随消息一起发出的图片文件名(放在插件根目录,可带子目录)"),
|
|
66
|
-
dataPath
|
|
67
|
-
|
|
66
|
+
// 🔴【重要改动 1】:明确 dataPath 是“目录路径”,使用 role('folder')
|
|
67
|
+
dataPath: import_koishi.Schema.string().description("数据存储目录(留空则使用默认目录)").default("").role("folder")
|
|
68
|
+
// 👈 Koishi Web 控制台会显示为“选择文件夹”
|
|
68
69
|
})
|
|
69
70
|
});
|
|
70
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
|
+
`⚔️ 物攻 / 魔攻: ${stats["攻擊力"] || "0"} / ${stats["魔法攻擊力"] || "0"}`,
|
|
132
|
+
// `🔮 魔攻: ${stats['魔法攻擊力'] || '0'}`,
|
|
133
|
+
`🎯 最终伤害: ${stats["最終傷害"] || "0"}%`,
|
|
134
|
+
`🧨 暴击伤害: ${stats["爆擊傷害"] || "0"}%`,
|
|
135
|
+
`👹 BOSS伤害: ${stats["BOSS怪物傷害"] || "0"}%`,
|
|
136
|
+
`👾 一般伤害: ${stats["一般怪物傷害"] || "0"}%`,
|
|
137
|
+
`💥 伤害: ${stats["傷害"] || "0"}%`,
|
|
138
|
+
`🛡️ 无视防御: ${stats["無視防禦率"] || "0"}%`,
|
|
139
|
+
`⭐ 星力: ${stats["星力"] || "0"}`,
|
|
140
|
+
`🌀 神秘力量(ARC): ${stats["神秘力量"] || "0"}`,
|
|
141
|
+
`✨ 真实力量(AUT): ${stats["真實之力"] || "0"}`,
|
|
142
|
+
`📦 道具掉落率: ${stats["道具掉落率"] || "0"}%`,
|
|
143
|
+
`💰 枫币获得量: ${stats["楓幣獲得量"] || "0"}%`,
|
|
144
|
+
`⏱️ 冷卻减少(秒): ${stats["冷卻時間減少(秒)"] || "0"}秒`
|
|
145
|
+
].join("\n");
|
|
146
|
+
return `${basic.characterName} (${basic.worldName}@${basic.characterGuildName || "无公会"})
|
|
147
|
+
${basic.characterClass} | Lv.${basic.characterLevel} (${basic.characterExpRate}%)
|
|
148
|
+
|
|
149
|
+
建立日期: ${createDate}
|
|
150
|
+
|
|
151
|
+
详细属性:
|
|
152
|
+
${statLines}
|
|
153
|
+
|
|
154
|
+
更多详细信息:
|
|
155
|
+
https://maplescouter.com/info?name=${encoded}`;
|
|
156
|
+
}
|
|
157
|
+
__name(formatCharacterInfo, "formatCharacterInfo");
|
|
71
158
|
function apply(ctx, config) {
|
|
72
159
|
if (!config.enabled) return;
|
|
73
160
|
const isAdmin = /* @__PURE__ */ __name((session) => config.admins.includes(session.userId), "isAdmin");
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
161
|
+
const DATA_DIR = (0, import_path.resolve)(
|
|
162
|
+
config.ms.dataPath?.trim() ? config.ms.dataPath.trim() : (0, import_path.resolve)(process.cwd(), "data", "msbao")
|
|
163
|
+
// 默认目录
|
|
164
|
+
);
|
|
165
|
+
const BINDINGS_FILE = (0, import_path.resolve)(DATA_DIR, "bindings.json");
|
|
166
|
+
if (!(0, import_fs.existsSync)(DATA_DIR)) {
|
|
167
|
+
(0, import_fs.mkdirSync)(DATA_DIR, { recursive: true });
|
|
78
168
|
}
|
|
79
169
|
let bindings = {};
|
|
80
|
-
if ((0, import_fs.existsSync)(
|
|
170
|
+
if ((0, import_fs.existsSync)(BINDINGS_FILE)) {
|
|
81
171
|
try {
|
|
82
|
-
bindings = JSON.parse((0, import_fs.readFileSync)(
|
|
172
|
+
bindings = JSON.parse((0, import_fs.readFileSync)(BINDINGS_FILE, "utf-8"));
|
|
83
173
|
} catch (e) {
|
|
84
174
|
console.error("读取绑定数据失败,使用空数据:", e);
|
|
85
175
|
bindings = {};
|
|
86
176
|
}
|
|
87
177
|
}
|
|
88
178
|
const saveBindings = /* @__PURE__ */ __name(() => {
|
|
89
|
-
(0, import_fs.writeFileSync)(
|
|
179
|
+
(0, import_fs.writeFileSync)(BINDINGS_FILE, JSON.stringify(bindings, null, 2), "utf-8");
|
|
90
180
|
}, "saveBindings");
|
|
91
181
|
const getBoundGameId = /* @__PURE__ */ __name((qqId) => {
|
|
92
182
|
return bindings[qqId] || null;
|
|
@@ -164,22 +254,15 @@ ${i.websites.join("\n")}
|
|
|
164
254
|
const api = new import_tms.MapleStoryApi(config.apiKey);
|
|
165
255
|
ctx.command("%查询 <name:string>", "查询TMS角色信息").alias("%查詢").action(async ({ session }, name2) => {
|
|
166
256
|
if (!canUse(session, config.ms)) return "";
|
|
167
|
-
if (!name2) return "请提供角色名, 用法: %查询 角色名
|
|
257
|
+
if (!name2) return "请提供角色名, 用法: %查询 角色名";
|
|
168
258
|
try {
|
|
169
259
|
const character = await api.getCharacter(name2);
|
|
170
260
|
const ocid = character.ocid;
|
|
171
261
|
if (!ocid) return "查询失败,请检查角色名";
|
|
172
|
-
|
|
173
|
-
if (!basic) return "查询失败,请检查角色名";
|
|
174
|
-
const encoded = encodeURIComponent(name2);
|
|
175
|
-
return `${basic.characterName} (${basic.worldName}@${basic.characterGuildName || "无公会"})
|
|
176
|
-
${basic.characterClass} | Lv.${basic.characterLevel} (${basic.characterExpRate + "%"})
|
|
177
|
-
|
|
178
|
-
详细信息:
|
|
179
|
-
https://maplescouter.com/info?name=${encoded}`;
|
|
262
|
+
return await formatCharacterInfo(api, ocid, name2);
|
|
180
263
|
} catch (err) {
|
|
181
264
|
if (err.constructor.name === "MapleStoryApiError") {
|
|
182
|
-
return
|
|
265
|
+
return `查询失败,请检查角色名`;
|
|
183
266
|
}
|
|
184
267
|
return `查询失败,请稍后再试或联系开发者(布丁@2482457432 )`;
|
|
185
268
|
}
|
|
@@ -189,40 +272,121 @@ https://maplescouter.com/info?name=${encoded}`;
|
|
|
189
272
|
const qqId = session.userId;
|
|
190
273
|
const currentGameId = getBoundGameId(qqId);
|
|
191
274
|
if (currentGameId) {
|
|
192
|
-
return
|
|
275
|
+
return `你的QQ号${qqId} 已与ID ${currentGameId} 绑定,如需换绑,先使用"%解绑"后再次绑定。`;
|
|
193
276
|
}
|
|
194
277
|
bindQQToGameId(qqId, gameId);
|
|
195
|
-
return
|
|
278
|
+
return `已成功将你的QQ号 ${qqId} 与ID ${gameId} 绑定`;
|
|
196
279
|
});
|
|
197
280
|
ctx.command("%我的信息", "查询绑定的游戏角色信息").action(async ({ session }) => {
|
|
198
281
|
const qqId = session.userId;
|
|
199
282
|
const boundGameId = getBoundGameId(qqId);
|
|
200
283
|
if (!boundGameId) {
|
|
201
|
-
return "
|
|
284
|
+
return "你尚未绑定角色名,快使用 %绑定 角色名 指令进行绑定吧";
|
|
202
285
|
}
|
|
203
286
|
if (!canUse(session, config.ms)) return "";
|
|
204
287
|
if (!config.apiKey) {
|
|
205
|
-
return "API
|
|
288
|
+
return "API密钥未设置,请联系开发者配置API密钥";
|
|
206
289
|
}
|
|
207
290
|
try {
|
|
208
291
|
const character = await api.getCharacter(boundGameId);
|
|
209
292
|
const ocid = character.ocid;
|
|
210
293
|
if (!ocid) return "查询失败,请检查角色名";
|
|
211
|
-
|
|
212
|
-
if (!basic) return "查询失败,请检查角色名";
|
|
213
|
-
const encoded = encodeURIComponent(boundGameId);
|
|
214
|
-
return `${basic.characterName} (${basic.worldName}@${basic.characterGuildName || "无公会"})
|
|
215
|
-
${basic.characterClass} | Lv.${basic.characterLevel} (${basic.characterExpRate + "%"})
|
|
216
|
-
|
|
217
|
-
详细信息:
|
|
218
|
-
https://maplescouter.com/info?name=${encoded}`;
|
|
294
|
+
return await formatCharacterInfo(api, ocid, boundGameId);
|
|
219
295
|
} catch (err) {
|
|
220
296
|
if (err.constructor.name === "MapleStoryApiError") {
|
|
221
|
-
return
|
|
297
|
+
return `查询失败,请检查角色名`;
|
|
222
298
|
}
|
|
223
299
|
return `查询失败,请稍后再试或联系开发者(布丁@2482457432 )`;
|
|
224
300
|
}
|
|
225
301
|
});
|
|
302
|
+
async function analyzeExpTrend(api2, ocid, queryInterval) {
|
|
303
|
+
const tst = new Date(Date.now() + 8 * 36e5);
|
|
304
|
+
function getTstDate(offsetDay) {
|
|
305
|
+
const d = new Date(tst.getTime() + offsetDay * 864e5);
|
|
306
|
+
d.setHours(0, 0, 0, 0);
|
|
307
|
+
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
308
|
+
}
|
|
309
|
+
__name(getTstDate, "getTstDate");
|
|
310
|
+
const dates = [null];
|
|
311
|
+
for (let i = 1; i <= 7; i++) dates.push(getTstDate(-i));
|
|
312
|
+
const basics = [];
|
|
313
|
+
for (let i = 0; i < dates.length; i++) {
|
|
314
|
+
const date = dates[i];
|
|
315
|
+
try {
|
|
316
|
+
await new Promise((r) => setTimeout(r, queryInterval));
|
|
317
|
+
const b = date === null ? await api2.getCharacterBasic(ocid) : await api2.getCharacterBasic(ocid, date);
|
|
318
|
+
basics.push(b);
|
|
319
|
+
} catch (e) {
|
|
320
|
+
basics.push(null);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const valid = basics.filter((b) => b);
|
|
324
|
+
if (valid.length < 2) throw new Error("网络错误(");
|
|
325
|
+
const records = [];
|
|
326
|
+
for (let i = 0; i < valid.length; i++) {
|
|
327
|
+
records.push({
|
|
328
|
+
level: valid[i].characterLevel,
|
|
329
|
+
expRate: Number(valid[i].characterExpRate),
|
|
330
|
+
label: i === 0 ? "目 前" : `${i}天前`
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
const gains = [];
|
|
334
|
+
for (let i = 0; i < records.length - 1; i++) {
|
|
335
|
+
const curr = records[i];
|
|
336
|
+
const prev = records[i + 1];
|
|
337
|
+
let gain = 0;
|
|
338
|
+
if (curr.level > prev.level) {
|
|
339
|
+
const levelDiff = curr.level - prev.level;
|
|
340
|
+
gain = 100 - prev.expRate + curr.expRate + (levelDiff - 1) * 100;
|
|
341
|
+
} else if (curr.level === prev.level) {
|
|
342
|
+
gain = curr.expRate - prev.expRate;
|
|
343
|
+
} else {
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
gains.push(gain);
|
|
347
|
+
records[i].gain = gain;
|
|
348
|
+
}
|
|
349
|
+
const start = records[records.length - 1];
|
|
350
|
+
const end = records[0];
|
|
351
|
+
let totalGrowthStr = "";
|
|
352
|
+
if (end.level > start.level) {
|
|
353
|
+
totalGrowthStr = `+${end.level - start.level} Lv`;
|
|
354
|
+
} else {
|
|
355
|
+
const totalGain = end.expRate - start.expRate;
|
|
356
|
+
totalGrowthStr = `${totalGain >= 0 ? "+" : ""}${totalGain.toFixed(3)}%`;
|
|
357
|
+
}
|
|
358
|
+
const avgGain = gains.length ? gains.reduce((a, b) => a + b, 0) / gains.length : 0;
|
|
359
|
+
const currentRate = end.expRate;
|
|
360
|
+
const gap = 100 - currentRate;
|
|
361
|
+
const predictDays = avgGain <= 0 ? "∞" : Math.max(1, Math.ceil(gap / avgGain)).toString();
|
|
362
|
+
const upgradeDate = new Date(Date.now() + parseInt(predictDays) * 864e5);
|
|
363
|
+
const upgradeStr = `${upgradeDate.getFullYear()}-${String(upgradeDate.getMonth() + 1).padStart(2, "0")}-${String(upgradeDate.getDate()).padStart(2, "0")}`;
|
|
364
|
+
let lines = `${valid[0].characterName}·${valid[0].characterClass} (${valid[0].worldName}@${valid[0].characterGuildName || "无公会"})
|
|
365
|
+
|
|
366
|
+
经验变化:
|
|
367
|
+
`;
|
|
368
|
+
for (let i = 0; i < records.length - 1; i++) {
|
|
369
|
+
const curr = records[i];
|
|
370
|
+
const gain = curr.gain;
|
|
371
|
+
if (gain !== void 0) {
|
|
372
|
+
const sign = gain >= 0 ? "+" : "";
|
|
373
|
+
lines += `${curr.label}: Lv.${curr.level} (${curr.expRate.toFixed(3)}%) [${sign}${gain.toFixed(3)}%]
|
|
374
|
+
`;
|
|
375
|
+
} else {
|
|
376
|
+
lines += `${curr.label}: Lv.${curr.level} (${curr.expRate.toFixed(3)}%)
|
|
377
|
+
`;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
lines += `------------------------------
|
|
381
|
+
近期成长: ${totalGrowthStr}
|
|
382
|
+
日均+${avgGain.toFixed(3)}% /天
|
|
383
|
+
预计升级还需: ${predictDays} 天
|
|
384
|
+
预计升级日期: ${upgradeStr}
|
|
385
|
+
|
|
386
|
+
(当日数据可能不准确,下午6点完成数据分割)`;
|
|
387
|
+
return lines.trimEnd();
|
|
388
|
+
}
|
|
389
|
+
__name(analyzeExpTrend, "analyzeExpTrend");
|
|
226
390
|
ctx.command("%我的经验", "查看绑定角色最近7天经验变化").alias("%我的經驗").action(async ({ session }) => {
|
|
227
391
|
const qqId = session.userId;
|
|
228
392
|
const boundGameId = getBoundGameId(qqId);
|
|
@@ -231,87 +395,26 @@ https://maplescouter.com/info?name=${encoded}`;
|
|
|
231
395
|
}
|
|
232
396
|
if (!canUse(session, config.ms)) return "";
|
|
233
397
|
if (!config.apiKey) {
|
|
234
|
-
return "API
|
|
398
|
+
return "API密钥未设置,请联系开发者配置API密钥";
|
|
235
399
|
}
|
|
236
400
|
try {
|
|
237
|
-
let getTstDate = function(offsetDay) {
|
|
238
|
-
const d = new Date(tst.getTime() + offsetDay * 864e5);
|
|
239
|
-
d.setHours(0, 0, 0, 0);
|
|
240
|
-
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
241
|
-
};
|
|
242
|
-
__name(getTstDate, "getTstDate");
|
|
243
401
|
const character = await api.getCharacter(boundGameId);
|
|
244
402
|
const ocid = character.ocid;
|
|
245
403
|
if (!ocid) return "查询失败,请检查角色名";
|
|
246
|
-
const
|
|
247
|
-
const dates = [null];
|
|
248
|
-
for (let i = 1; i <= 7; i++) dates.push(getTstDate(-i));
|
|
249
|
-
const basics = [];
|
|
250
|
-
for (let i = 0; i < dates.length; i++) {
|
|
251
|
-
const date = dates[i];
|
|
252
|
-
const dateStr = date ? `${date.year}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}` : "latest";
|
|
253
|
-
try {
|
|
254
|
-
await new Promise((r) => setTimeout(r, config.ms.queryInterval ?? 100));
|
|
255
|
-
const b = date === null ? await api.getCharacterBasic(ocid) : await api.getCharacterBasic(ocid, date);
|
|
256
|
-
basics.push(b);
|
|
257
|
-
} catch (e) {
|
|
258
|
-
basics.push(null);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
const valid = basics.filter((b) => b);
|
|
262
|
-
if (valid.length < 2) return "网络错误(";
|
|
263
|
-
const dailyDiffs = [];
|
|
264
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
265
|
-
const curr = valid[i];
|
|
266
|
-
const prev = valid[i + 1];
|
|
267
|
-
if (curr.characterLevel === prev.characterLevel) {
|
|
268
|
-
dailyDiffs.push(Number(curr.characterExpRate) - Number(prev.characterExpRate));
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const avgDiff = dailyDiffs.length ? dailyDiffs.reduce((a, b) => a + b, 0) / dailyDiffs.length : 0;
|
|
272
|
-
const avgDiffStr = avgDiff.toFixed(3);
|
|
273
|
-
const currentRate = Number(valid[0].characterExpRate);
|
|
274
|
-
const gap = 100 - currentRate;
|
|
275
|
-
const gapStr = gap.toFixed(3);
|
|
276
|
-
const predictDays = avgDiff <= 0 ? "∞" : Math.max(1, Math.ceil(gap / avgDiff)).toString();
|
|
277
|
-
const upgradeDate = new Date(Date.now() + parseInt(predictDays) * 864e5);
|
|
278
|
-
const upgradeStr = `${upgradeDate.getFullYear()}-${String(upgradeDate.getMonth() + 1).padStart(2, "0")}-${String(upgradeDate.getDate()).padStart(2, "0")}`;
|
|
279
|
-
const head = valid[0];
|
|
280
|
-
let lines = `${head.characterName}·${head.characterClass} (${head.worldName}@${head.characterGuildName || "无公会"})
|
|
281
|
-
经验变化:
|
|
282
|
-
`;
|
|
283
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
284
|
-
const curr = valid[i];
|
|
285
|
-
const prev = valid[i + 1];
|
|
286
|
-
if (curr.characterLevel > prev.characterLevel) {
|
|
287
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)
|
|
288
|
-
`;
|
|
289
|
-
} else {
|
|
290
|
-
const diff = (Number(curr.characterExpRate) - Number(prev.characterExpRate)).toFixed(3);
|
|
291
|
-
const sign = diff.startsWith("-") ? "" : "+";
|
|
292
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)[${sign}${diff}%]
|
|
293
|
-
`;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
lines += `----------------------
|
|
297
|
-
日均+${avgDiffStr}%/天
|
|
298
|
-
预计升级还需: ${predictDays} 天
|
|
299
|
-
预计升级日期: ${upgradeStr}
|
|
300
|
-
|
|
301
|
-
(如若升级则不计算日均增长,可能出现预计数据报错)
|
|
302
|
-
(当日数据可能不准确,下午6点完成更新)`;
|
|
404
|
+
const resultText = await analyzeExpTrend(api, ocid, config.ms.queryInterval ?? 100);
|
|
303
405
|
const candidates = config.ms.images?.map((s) => s.trim()).filter(Boolean) || [];
|
|
304
406
|
const existFiles = candidates.map((f) => (0, import_path.resolve)(process.cwd(), f)).filter((f) => (0, import_fs.existsSync)(f));
|
|
305
407
|
if (existFiles.length) {
|
|
306
408
|
const picked = existFiles[Math.floor(Math.random() * existFiles.length)];
|
|
307
409
|
return [
|
|
308
|
-
|
|
410
|
+
resultText,
|
|
309
411
|
import_koishi.h.image((0, import_url.pathToFileURL)(picked).href)
|
|
310
412
|
];
|
|
311
413
|
}
|
|
312
|
-
return
|
|
414
|
+
return resultText;
|
|
313
415
|
} catch (err) {
|
|
314
|
-
if (err.
|
|
416
|
+
if (err.message === "网络错误(") return "网络错误(";
|
|
417
|
+
if (err.constructor.name === "MapleStoryApiError") return "查询失败,请检查角色名";
|
|
315
418
|
return "查询失败,请稍后再试或联系开发者(布丁@2482457432 )";
|
|
316
419
|
}
|
|
317
420
|
});
|
|
@@ -319,87 +422,26 @@ https://maplescouter.com/info?name=${encoded}`;
|
|
|
319
422
|
if (!canUse(session, config.ms)) return "";
|
|
320
423
|
if (!name2) return "请提供角色名";
|
|
321
424
|
if (!config.apiKey) {
|
|
322
|
-
return "API
|
|
425
|
+
return "API密钥未设置,请联系开发者配置API密钥";
|
|
323
426
|
}
|
|
324
427
|
try {
|
|
325
|
-
let getTstDate = function(offsetDay) {
|
|
326
|
-
const d = new Date(tst.getTime() + offsetDay * 864e5);
|
|
327
|
-
d.setHours(0, 0, 0, 0);
|
|
328
|
-
return { year: d.getUTCFullYear(), month: d.getUTCMonth() + 1, day: d.getUTCDate() };
|
|
329
|
-
};
|
|
330
|
-
__name(getTstDate, "getTstDate");
|
|
331
428
|
const character = await api.getCharacter(name2);
|
|
332
429
|
const ocid = character.ocid;
|
|
333
430
|
if (!ocid) return "查询失败,请检查角色名";
|
|
334
|
-
const
|
|
335
|
-
const dates = [null];
|
|
336
|
-
for (let i = 1; i <= 7; i++) dates.push(getTstDate(-i));
|
|
337
|
-
const basics = [];
|
|
338
|
-
for (let i = 0; i < dates.length; i++) {
|
|
339
|
-
const date = dates[i];
|
|
340
|
-
const dateStr = date ? `${date.year}-${String(date.month).padStart(2, "0")}-${String(date.day).padStart(2, "0")}` : "latest";
|
|
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 avgDiffStr = avgDiff.toFixed(3);
|
|
361
|
-
const currentRate = Number(valid[0].characterExpRate);
|
|
362
|
-
const gap = 100 - currentRate;
|
|
363
|
-
const gapStr = gap.toFixed(3);
|
|
364
|
-
const predictDays = avgDiff <= 0 ? "∞" : Math.max(1, Math.ceil(gap / avgDiff)).toString();
|
|
365
|
-
const upgradeDate = new Date(Date.now() + parseInt(predictDays) * 864e5);
|
|
366
|
-
const upgradeStr = `${upgradeDate.getFullYear()}-${String(upgradeDate.getMonth() + 1).padStart(2, "0")}-${String(upgradeDate.getDate()).padStart(2, "0")}`;
|
|
367
|
-
const head = valid[0];
|
|
368
|
-
let lines = `${head.characterName}·${head.characterClass} (${head.worldName}@${head.characterGuildName || "无公会"})
|
|
369
|
-
经验变化:
|
|
370
|
-
`;
|
|
371
|
-
for (let i = 0; i < valid.length - 1; i++) {
|
|
372
|
-
const curr = valid[i];
|
|
373
|
-
const prev = valid[i + 1];
|
|
374
|
-
if (curr.characterLevel > prev.characterLevel) {
|
|
375
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)
|
|
376
|
-
`;
|
|
377
|
-
} else {
|
|
378
|
-
const diff = (Number(curr.characterExpRate) - Number(prev.characterExpRate)).toFixed(3);
|
|
379
|
-
const sign = diff.startsWith("-") ? "" : "+";
|
|
380
|
-
lines += `${i === 0 ? "目 前" : `${i}天前`}: Lv.${curr.characterLevel} (${curr.characterExpRate}%)[${sign}${diff}%]
|
|
381
|
-
`;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
lines += `----------------------
|
|
385
|
-
日均+${avgDiffStr}%/天
|
|
386
|
-
预计升级还需: ${predictDays} 天
|
|
387
|
-
预计升级日期: ${upgradeStr}
|
|
388
|
-
|
|
389
|
-
(如若升级则不计算日均增长,可能出现预计数据报错)
|
|
390
|
-
(当日数据可能不准确,下午6点完成更新)`;
|
|
431
|
+
const resultText = await analyzeExpTrend(api, ocid, config.ms.queryInterval ?? 100);
|
|
391
432
|
const candidates = config.ms.images?.map((s) => s.trim()).filter(Boolean) || [];
|
|
392
433
|
const existFiles = candidates.map((f) => (0, import_path.resolve)(process.cwd(), f)).filter((f) => (0, import_fs.existsSync)(f));
|
|
393
434
|
if (existFiles.length) {
|
|
394
435
|
const picked = existFiles[Math.floor(Math.random() * existFiles.length)];
|
|
395
436
|
return [
|
|
396
|
-
|
|
437
|
+
resultText,
|
|
397
438
|
import_koishi.h.image((0, import_url.pathToFileURL)(picked).href)
|
|
398
439
|
];
|
|
399
440
|
}
|
|
400
|
-
return
|
|
441
|
+
return resultText;
|
|
401
442
|
} catch (err) {
|
|
402
|
-
if (err.
|
|
443
|
+
if (err.message === "网络错误(") return "网络错误(";
|
|
444
|
+
if (err.constructor.name === "MapleStoryApiError") return "查询失败,请检查角色名";
|
|
403
445
|
return "查询失败,请稍后再试或联系开发者(布丁@2482457432 )";
|
|
404
446
|
}
|
|
405
447
|
});
|
|
@@ -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.17",
|
|
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
|
+
}
|