koishi-plugin-cfmrmod 1.0.5 → 1.0.7
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/README.md +3 -0
- package/dist/index.js +1 -0
- package/dist/mcmod/index.js +2 -2
- package/dist/notify.js +53 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# koishi-plugin-cfmrmod
|
|
2
2
|
|
|
3
3
|
Koishi 插件:搜索 CurseForge / Modrinth / MCMod,并渲染图片卡片。
|
|
4
|
+
哥给个star吧
|
|
5
|
+
Star就是我维护的动力🤤
|
|
4
6
|
|
|
5
7
|
## 使用方法
|
|
6
8
|
|
|
@@ -41,6 +43,7 @@ Koishi 插件:搜索 CurseForge / Modrinth / MCMod,并渲染图片卡片。
|
|
|
41
43
|
- `notify.interval`: 全局轮询间隔(毫秒)
|
|
42
44
|
- `notify.adminAuthority`: 权限等级(1=全部,2=管理员+群主,3=仅群主)
|
|
43
45
|
- `notify.stateFile`: 状态文件路径(JSON)
|
|
46
|
+
- `notify.configFile`: 订阅配置文件路径(JSON,指令修改会写入)
|
|
44
47
|
- `notify.groups`: 通知群组列表
|
|
45
48
|
- `channelId`: 群/频道 ID
|
|
46
49
|
- `enabled`: 是否启用本群通知
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
52
52
|
interval: koishi_1.Schema.number().default(30 * 60 * 1000).description('轮询间隔(ms),默认 30 分钟'),
|
|
53
53
|
adminAuthority: koishi_1.Schema.number().default(3).description('机器人管理员权限等级(默认 3)'),
|
|
54
54
|
stateFile: koishi_1.Schema.string().default('data/cfmrmod_notify_state.json').description('状态存储 JSON 路径(数据库不可用时使用)'),
|
|
55
|
+
configFile: koishi_1.Schema.string().default('data/cfmrmod_notify_config.json').description('订阅配置存储 JSON 路径(指令修改会写入)'),
|
|
55
56
|
groups: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
56
57
|
channelId: koishi_1.Schema.string().description('群号/频道 ID'),
|
|
57
58
|
enabled: koishi_1.Schema.boolean().default(true).description('是否启用本群通知'),
|
package/dist/mcmod/index.js
CHANGED
|
@@ -564,7 +564,7 @@ async function drawModCard(url) {
|
|
|
564
564
|
versions.forEach(v => {
|
|
565
565
|
dummy.font = `14px "${font}"`;
|
|
566
566
|
const lw = dummy.measureText(v.l).width + 10;
|
|
567
|
-
const lines = wrapText(dummy, v.v, 0, 0, contentW - lw, 20,
|
|
567
|
+
const lines = wrapText(dummy, v.v, 0, 0, contentW - lw, 20, 500, false) / 20;
|
|
568
568
|
extraH += lines * 20 + 10;
|
|
569
569
|
});
|
|
570
570
|
}
|
|
@@ -578,7 +578,7 @@ async function drawModCard(url) {
|
|
|
578
578
|
const isHeader = node.tag === 'h';
|
|
579
579
|
dummy.font = `${isHeader ? 'bold' : ''} ${isHeader ? 22 : 16}px "${font}"`;
|
|
580
580
|
const lh = isHeader ? 32 : 26;
|
|
581
|
-
const lines = wrapText(dummy, node.val, 0, 0, contentW, lh,
|
|
581
|
+
const lines = wrapText(dummy, node.val, 0, 0, contentW, lh, 5000, false) / lh;
|
|
582
582
|
descH += lines * lh + (isHeader ? 15 : 10);
|
|
583
583
|
}
|
|
584
584
|
else if (node.type === 'i') {
|
package/dist/notify.js
CHANGED
|
@@ -183,6 +183,7 @@ function apply(ctx, config, options) {
|
|
|
183
183
|
let stateLoaded = false;
|
|
184
184
|
let saving = false;
|
|
185
185
|
let dbWarned = false;
|
|
186
|
+
let configLoaded = false;
|
|
186
187
|
const getStateKey = (channelId, platform, projectId) => {
|
|
187
188
|
return `${channelId}|${platform}|${projectId}`;
|
|
188
189
|
};
|
|
@@ -190,6 +191,48 @@ function apply(ctx, config, options) {
|
|
|
190
191
|
const p = String(config.stateFile || 'data/cfmrmod_notify_state.json');
|
|
191
192
|
return path_1.default.isAbsolute(p) ? p : path_1.default.resolve(process.cwd(), p);
|
|
192
193
|
};
|
|
194
|
+
const resolveConfigFile = () => {
|
|
195
|
+
const p = String(config.configFile || 'data/cfmrmod_notify_config.json');
|
|
196
|
+
return path_1.default.isAbsolute(p) ? p : path_1.default.resolve(process.cwd(), p);
|
|
197
|
+
};
|
|
198
|
+
const loadConfigFromFile = async () => {
|
|
199
|
+
if (configLoaded)
|
|
200
|
+
return;
|
|
201
|
+
configLoaded = true;
|
|
202
|
+
try {
|
|
203
|
+
const filePath = resolveConfigFile();
|
|
204
|
+
const content = await fs_1.promises.readFile(filePath, 'utf8');
|
|
205
|
+
const json = JSON.parse(content);
|
|
206
|
+
if (json && typeof json === 'object') {
|
|
207
|
+
if (typeof json.enabled === 'boolean')
|
|
208
|
+
config.enabled = json.enabled;
|
|
209
|
+
if (Array.isArray(json.groups))
|
|
210
|
+
config.groups = json.groups;
|
|
211
|
+
}
|
|
212
|
+
if (!Array.isArray(config.groups))
|
|
213
|
+
config.groups = [];
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
if (!Array.isArray(config.groups))
|
|
217
|
+
config.groups = [];
|
|
218
|
+
if (config.groups.length)
|
|
219
|
+
await saveConfigToFile();
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
const saveConfigToFile = async () => {
|
|
223
|
+
try {
|
|
224
|
+
const filePath = resolveConfigFile();
|
|
225
|
+
await fs_1.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
|
|
226
|
+
const obj = {
|
|
227
|
+
enabled: !!config.enabled,
|
|
228
|
+
groups: Array.isArray(config.groups) ? config.groups : [],
|
|
229
|
+
};
|
|
230
|
+
await fs_1.promises.writeFile(filePath, JSON.stringify(obj, null, 2), 'utf8');
|
|
231
|
+
}
|
|
232
|
+
catch (e) {
|
|
233
|
+
logger.warn(`配置文件写入失败:${e.message}`);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
193
236
|
const loadStateFromFile = async () => {
|
|
194
237
|
if (stateLoaded)
|
|
195
238
|
return;
|
|
@@ -387,6 +430,7 @@ function apply(ctx, config, options) {
|
|
|
387
430
|
}
|
|
388
431
|
async function checkOnce(channelId, force = false) {
|
|
389
432
|
var _a;
|
|
433
|
+
await loadConfigFromFile();
|
|
390
434
|
if (!config.enabled)
|
|
391
435
|
return;
|
|
392
436
|
const subs = getConfigSubs(channelId);
|
|
@@ -437,6 +481,7 @@ function apply(ctx, config, options) {
|
|
|
437
481
|
}
|
|
438
482
|
const checkOne = async (sub, forceSendAll) => {
|
|
439
483
|
var _a;
|
|
484
|
+
await loadConfigFromFile();
|
|
440
485
|
const timeout = ((_a = options === null || options === void 0 ? void 0 : options.cfmr) === null || _a === void 0 ? void 0 : _a.requestTimeout) || 15000;
|
|
441
486
|
const latest = sub.platform === 'mr'
|
|
442
487
|
? await getLatestModrinth(sub.projectId, timeout)
|
|
@@ -474,6 +519,7 @@ function apply(ctx, config, options) {
|
|
|
474
519
|
}
|
|
475
520
|
ctx.command('notify.add <platform> <projectId>', '添加更新订阅')
|
|
476
521
|
.action(async ({ session }, platform, projectId) => {
|
|
522
|
+
await loadConfigFromFile();
|
|
477
523
|
if (!platform || !projectId)
|
|
478
524
|
return '参数不足。';
|
|
479
525
|
const platformKey = normalizePlatform(platform);
|
|
@@ -495,10 +541,12 @@ function apply(ctx, config, options) {
|
|
|
495
541
|
const interval = Math.max(60 * 1000, Number(config.interval) || 30 * 60 * 1000);
|
|
496
542
|
list.push({ platform: platformKey, projectId: pid, interval });
|
|
497
543
|
group.subs = list;
|
|
544
|
+
await saveConfigToFile();
|
|
498
545
|
return `已添加订阅:${platformKey}:${pid}`;
|
|
499
546
|
});
|
|
500
547
|
ctx.command('notify.remove <platform> <projectId>', '删除更新订阅')
|
|
501
548
|
.action(async ({ session }, platform, projectId) => {
|
|
549
|
+
await loadConfigFromFile();
|
|
502
550
|
if (!platform || !projectId)
|
|
503
551
|
return '参数不足。';
|
|
504
552
|
const platformKey = normalizePlatform(platform);
|
|
@@ -519,10 +567,12 @@ function apply(ctx, config, options) {
|
|
|
519
567
|
group.subs = group.subs.filter((s) => !(normalizePlatform(s === null || s === void 0 ? void 0 : s.platform) === platformKey && String((s === null || s === void 0 ? void 0 : s.projectId) || '').trim() === pid));
|
|
520
568
|
if (group.subs.length === before)
|
|
521
569
|
return '未找到订阅。';
|
|
570
|
+
await saveConfigToFile();
|
|
522
571
|
return `已删除订阅:${platformKey}:${pid}`;
|
|
523
572
|
});
|
|
524
573
|
ctx.command('notify.list', '列出订阅')
|
|
525
574
|
.action(async ({ session }) => {
|
|
575
|
+
await loadConfigFromFile();
|
|
526
576
|
const targetChannel = session.channelId;
|
|
527
577
|
if (!targetChannel)
|
|
528
578
|
return '只能在群聊使用或指定 channelId。';
|
|
@@ -546,6 +596,7 @@ function apply(ctx, config, options) {
|
|
|
546
596
|
});
|
|
547
597
|
ctx.command('notify.enable <onoff>', '启用/禁用本群通知')
|
|
548
598
|
.action(async ({ session }, onoff) => {
|
|
599
|
+
await loadConfigFromFile();
|
|
549
600
|
const targetChannel = session.channelId;
|
|
550
601
|
if (!targetChannel)
|
|
551
602
|
return '只能在群聊使用或指定 channelId。';
|
|
@@ -556,6 +607,7 @@ function apply(ctx, config, options) {
|
|
|
556
607
|
return 'onoff 参数错误,请使用 on/off 或 true/false。';
|
|
557
608
|
const group = ensureGroup(targetChannel);
|
|
558
609
|
group.enabled = flag;
|
|
610
|
+
await saveConfigToFile();
|
|
559
611
|
return flag ? '已启用本群通知。' : '已禁用本群通知。';
|
|
560
612
|
});
|
|
561
613
|
ctx.command('notify.helpme', '查看通知系统帮助')
|
|
@@ -579,6 +631,7 @@ function apply(ctx, config, options) {
|
|
|
579
631
|
ctx.command('notify.check [arg]', '手动检查更新')
|
|
580
632
|
.option('broadcast', '-b 直接发送最新版卡片(忽略是否更新)')
|
|
581
633
|
.action(async ({ session, options }, arg) => {
|
|
634
|
+
await loadConfigFromFile();
|
|
582
635
|
const targetChannel = session.channelId;
|
|
583
636
|
if (!targetChannel)
|
|
584
637
|
return '只能在群聊使用或指定 channelId。';
|