koishi-plugin-forward-hime 1.4.1-alpha.2 → 1.4.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/index.js +45 -0
- package/lib/console.d.ts +2 -0
- package/lib/decorator.d.ts +1 -1
- package/lib/diagnostics.d.ts +18 -0
- package/lib/index.js +294 -47
- package/lib/relay.d.ts +3 -1
- package/package.json +3 -2
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// client/index.ts
|
|
2
|
+
import { send } from "@koishijs/client";
|
|
3
|
+
import { defineComponent, h, inject } from "vue";
|
|
4
|
+
var index_default = (ctx) => {
|
|
5
|
+
ctx.slot({
|
|
6
|
+
type: "plugin-details",
|
|
7
|
+
order: -800,
|
|
8
|
+
component: defineComponent({
|
|
9
|
+
setup() {
|
|
10
|
+
const current = inject(
|
|
11
|
+
"manager.settings.current"
|
|
12
|
+
);
|
|
13
|
+
return () => {
|
|
14
|
+
const plugin = current?.value;
|
|
15
|
+
if (!plugin || plugin.name !== "forward-hime" && !plugin.path?.includes("forward-hime")) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return h(
|
|
19
|
+
"button",
|
|
20
|
+
{
|
|
21
|
+
class: "k-button",
|
|
22
|
+
onClick: async () => {
|
|
23
|
+
const contents = await send(
|
|
24
|
+
"forward-hime:get-diagnostics"
|
|
25
|
+
);
|
|
26
|
+
const url = URL.createObjectURL(
|
|
27
|
+
new Blob([contents], { type: "application/x-ndjson" })
|
|
28
|
+
);
|
|
29
|
+
const anchor = document.createElement("a");
|
|
30
|
+
anchor.href = url;
|
|
31
|
+
anchor.download = "forward-hime-diagnostics.jsonl";
|
|
32
|
+
anchor.click();
|
|
33
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"\u4E0B\u8F7D\u8BCA\u65AD\u65E5\u5FD7"
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
export {
|
|
44
|
+
index_default as default
|
|
45
|
+
};
|
package/lib/console.d.ts
ADDED
package/lib/decorator.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare function decoratorInit(cfg: ConfigSet): void;
|
|
|
8
8
|
export declare function defaultDecorator({ head, content }: ForwardMsg): Element[];
|
|
9
9
|
export declare function defaultMiddleware(session: Session): ForwardMsg;
|
|
10
10
|
export declare function MsgMiddlewareCache(session: Session): Promise<void>;
|
|
11
|
-
export declare function MsgDecorator(session: Session, node: ForwardNode): Promise<any>;
|
|
11
|
+
export declare function MsgDecorator(session: Session, node: ForwardNode, traceId?: string): Promise<any>;
|
|
12
12
|
export declare function MsgDecoratorNoRelay(session: Session, node: ForwardNode): Promise<any>;
|
|
13
13
|
export declare function MsgDecoratorFallback(session: Session, node: ForwardNode): Promise<Element[]>;
|
|
14
14
|
export declare function MsgDecoratorFallbackReason(session: Session, node: ForwardNode, reason: string): Promise<Element[]>;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Context } from "koishi";
|
|
2
|
+
export interface DiagnosticEvent {
|
|
3
|
+
traceId: string;
|
|
4
|
+
phase: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export declare function createTraceId(): `${string}-${string}-${string}-${string}-${string}`;
|
|
8
|
+
export declare function diagnosticFilename(value: unknown): string;
|
|
9
|
+
export declare function diagnosticMime(value: unknown): string;
|
|
10
|
+
export declare function diagnosticsInit(ctx: Context): void;
|
|
11
|
+
export declare function writeDiagnostic(event: DiagnosticEvent): void;
|
|
12
|
+
export declare function readDiagnosticLogs(): Promise<string>;
|
|
13
|
+
export declare function sanitizeError(error: unknown): {
|
|
14
|
+
name: string;
|
|
15
|
+
code: string | number;
|
|
16
|
+
httpStatus: number;
|
|
17
|
+
summary: string;
|
|
18
|
+
};
|
package/lib/index.js
CHANGED
|
@@ -250,6 +250,99 @@ var onebot_default = {
|
|
|
250
250
|
|
|
251
251
|
// src/relay.ts
|
|
252
252
|
var import_koishi5 = require("koishi");
|
|
253
|
+
|
|
254
|
+
// src/diagnostics.ts
|
|
255
|
+
var import_crypto = require("crypto");
|
|
256
|
+
var import_promises = require("fs/promises");
|
|
257
|
+
var import_path = require("path");
|
|
258
|
+
var RETENTION_DAYS = 7;
|
|
259
|
+
var FILE_PATTERN = /^\d{4}-\d{2}-\d{2}\.jsonl$/;
|
|
260
|
+
var root = "";
|
|
261
|
+
var writeQueue = Promise.resolve();
|
|
262
|
+
var lastCleanupDate = "";
|
|
263
|
+
function createTraceId() {
|
|
264
|
+
return (0, import_crypto.randomUUID)();
|
|
265
|
+
}
|
|
266
|
+
function diagnosticFilename(value) {
|
|
267
|
+
if (typeof value !== "string" || /^https?:\/\//i.test(value)) return void 0;
|
|
268
|
+
return value.replace(/[?#].*$/, "").split(/[\\/]/).pop()?.slice(0, 120);
|
|
269
|
+
}
|
|
270
|
+
function diagnosticMime(value) {
|
|
271
|
+
return typeof value === "string" && /^[\w.+-]+\/[\w.+-]+$/.test(value) ? value : void 0;
|
|
272
|
+
}
|
|
273
|
+
function utcDate(timestamp = Date.now()) {
|
|
274
|
+
return new Date(timestamp).toISOString().slice(0, 10);
|
|
275
|
+
}
|
|
276
|
+
async function cleanup(date) {
|
|
277
|
+
if (date === lastCleanupDate) return;
|
|
278
|
+
const cutoff = Date.parse(`${date}T00:00:00.000Z`) - (RETENTION_DAYS - 1) * 864e5;
|
|
279
|
+
for (const filename of await (0, import_promises.readdir)(root)) {
|
|
280
|
+
if (!FILE_PATTERN.test(filename)) continue;
|
|
281
|
+
if (Date.parse(`${filename.slice(0, 10)}T00:00:00.000Z`) < cutoff) {
|
|
282
|
+
await (0, import_promises.rm)((0, import_path.join)(root, filename), { force: true });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
lastCleanupDate = date;
|
|
286
|
+
}
|
|
287
|
+
function diagnosticsInit(ctx2) {
|
|
288
|
+
root = (0, import_path.join)(ctx2.baseDir, "data", "forward-hime");
|
|
289
|
+
writeQueue = Promise.resolve();
|
|
290
|
+
lastCleanupDate = "";
|
|
291
|
+
writeQueue = writeQueue.then(async () => {
|
|
292
|
+
await (0, import_promises.mkdir)(root, { recursive: true });
|
|
293
|
+
await cleanup(utcDate());
|
|
294
|
+
}).catch((error) => logger.warn("failed to initialize diagnostic log", error));
|
|
295
|
+
}
|
|
296
|
+
function writeDiagnostic(event) {
|
|
297
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
298
|
+
const date = timestamp.slice(0, 10);
|
|
299
|
+
const line = JSON.stringify({ timestamp, ...event }) + "\n";
|
|
300
|
+
writeQueue = writeQueue.then(async () => {
|
|
301
|
+
await (0, import_promises.mkdir)(root, { recursive: true });
|
|
302
|
+
await cleanup(date);
|
|
303
|
+
await (0, import_promises.appendFile)((0, import_path.join)(root, `${date}.jsonl`), line, "utf8");
|
|
304
|
+
}).catch((error) => logger.warn("failed to write diagnostic log", error));
|
|
305
|
+
}
|
|
306
|
+
async function readDiagnosticLogs() {
|
|
307
|
+
await writeQueue;
|
|
308
|
+
const cutoff = new Date(
|
|
309
|
+
Date.parse(`${utcDate()}T00:00:00.000Z`) - (RETENTION_DAYS - 1) * 864e5
|
|
310
|
+
).toISOString().slice(0, 10);
|
|
311
|
+
const files = (await (0, import_promises.readdir)(root).catch(() => [])).filter(
|
|
312
|
+
(filename) => FILE_PATTERN.test(filename) && filename.slice(0, 10) >= cutoff
|
|
313
|
+
).sort();
|
|
314
|
+
const contents = await Promise.all(
|
|
315
|
+
files.map((filename) => (0, import_promises.readFile)((0, import_path.join)(root, filename), "utf8"))
|
|
316
|
+
);
|
|
317
|
+
return contents.join("");
|
|
318
|
+
}
|
|
319
|
+
function sanitizeError(error) {
|
|
320
|
+
const value = error;
|
|
321
|
+
const message = typeof value?.message === "string" ? value.message : String(error);
|
|
322
|
+
const lowerMessage = message.toLowerCase();
|
|
323
|
+
let summary = "request failed";
|
|
324
|
+
if (lowerMessage.includes("timeout") || lowerMessage.includes("timed out")) {
|
|
325
|
+
summary = "request timed out";
|
|
326
|
+
} else if (lowerMessage.includes("denied") || lowerMessage.includes("401") || lowerMessage.includes("403")) {
|
|
327
|
+
summary = "request denied";
|
|
328
|
+
} else if (lowerMessage.includes("not found") || lowerMessage.includes("404")) {
|
|
329
|
+
summary = "resource not found";
|
|
330
|
+
} else if (lowerMessage.includes("too large")) {
|
|
331
|
+
summary = "media exceeds size limit";
|
|
332
|
+
} else if (lowerMessage.includes("network") || lowerMessage.includes("econn") || lowerMessage.includes("enotfound")) {
|
|
333
|
+
summary = "network error";
|
|
334
|
+
} else if (lowerMessage.includes("empty media")) {
|
|
335
|
+
summary = "empty media payload";
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
name: typeof value?.name === "string" && /^[a-z][\w.-]{0,63}$/i.test(value.name) ? value.name : "Error",
|
|
339
|
+
code: typeof value?.code === "string" && /^[\w.-]{1,64}$/.test(value.code) || typeof value?.code === "number" ? value.code : void 0,
|
|
340
|
+
httpStatus: typeof value?.status === "number" ? value.status : Number(/\bstatus\s+(\d{3})\b/i.exec(message)?.[1]) || void 0,
|
|
341
|
+
summary
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/relay.ts
|
|
253
346
|
var DEFAULT_IMAGE_MIME = "image/jpeg";
|
|
254
347
|
var DEFAULT_AUDIO_MIME = "audio/mpeg";
|
|
255
348
|
var DEFAULT_VIDEO_MIME = "video/mp4";
|
|
@@ -340,8 +433,11 @@ function createRelayElement(kind, mime, buffer, filename) {
|
|
|
340
433
|
if (kind === "img") {
|
|
341
434
|
return import_koishi5.h.image(buffer, mime);
|
|
342
435
|
}
|
|
436
|
+
if (kind === "file") {
|
|
437
|
+
return helperMap.file(buffer, mime, { filename });
|
|
438
|
+
}
|
|
343
439
|
if (helperMap[kind]) {
|
|
344
|
-
return helperMap[kind](buffer, mime
|
|
440
|
+
return helperMap[kind](buffer, mime);
|
|
345
441
|
}
|
|
346
442
|
const base64 = buffer.toString("base64");
|
|
347
443
|
return (0, import_koishi5.h)(kind, { src: `data:${mime};base64,${base64}`, filename });
|
|
@@ -361,9 +457,23 @@ function networkMessageFromError(error) {
|
|
|
361
457
|
}
|
|
362
458
|
return "\u7F51\u7EDC\u95EE\u9898\uFF1A\u5A92\u4F53\u6587\u4EF6\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u3002";
|
|
363
459
|
}
|
|
364
|
-
async function downloadAndRelay(element) {
|
|
460
|
+
async function downloadAndRelay(element, traceId, session, node) {
|
|
461
|
+
const startedAt = Date.now();
|
|
365
462
|
const kind = normalizeMediaType(element.type);
|
|
366
463
|
const src = mediaUrlFromElement(element);
|
|
464
|
+
const media = {
|
|
465
|
+
type: kind,
|
|
466
|
+
filename: diagnosticFilename(element.attrs?.filename),
|
|
467
|
+
mime: diagnosticMime(element.attrs?.mime),
|
|
468
|
+
size: typeof element.attrs?.size === "number" ? element.attrs.size : void 0
|
|
469
|
+
};
|
|
470
|
+
const context = {
|
|
471
|
+
source: session && { platform: session.platform, node: session.channelId },
|
|
472
|
+
target: node && { platform: node.Platform, node: node.Guild }
|
|
473
|
+
};
|
|
474
|
+
if (traceId) {
|
|
475
|
+
writeDiagnostic({ traceId, phase: "media-relay-start", media, ...context });
|
|
476
|
+
}
|
|
367
477
|
if (!src) {
|
|
368
478
|
throw new MediaRelayError(
|
|
369
479
|
`missing media src for ${element.type}`,
|
|
@@ -374,6 +484,17 @@ async function downloadAndRelay(element) {
|
|
|
374
484
|
const cacheKey = getCacheKey(element);
|
|
375
485
|
const cacheItem = relayCache.get(cacheKey);
|
|
376
486
|
if (cacheItem) {
|
|
487
|
+
if (traceId) {
|
|
488
|
+
writeDiagnostic({
|
|
489
|
+
traceId,
|
|
490
|
+
phase: "media-relay-result",
|
|
491
|
+
status: "cache-hit",
|
|
492
|
+
bytes: cacheItem.buffer.length,
|
|
493
|
+
mime: diagnosticMime(cacheItem.mime),
|
|
494
|
+
durationMs: Date.now() - startedAt,
|
|
495
|
+
...context
|
|
496
|
+
});
|
|
497
|
+
}
|
|
377
498
|
return relayElementFromCache(cacheItem);
|
|
378
499
|
}
|
|
379
500
|
const timeoutSignal = typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(relayConfig.RequestTimeoutSec * 1e3) : void 0;
|
|
@@ -435,9 +556,21 @@ async function downloadAndRelay(element) {
|
|
|
435
556
|
buffer,
|
|
436
557
|
filename
|
|
437
558
|
});
|
|
559
|
+
if (traceId) {
|
|
560
|
+
writeDiagnostic({
|
|
561
|
+
traceId,
|
|
562
|
+
phase: "media-relay-result",
|
|
563
|
+
status: "success",
|
|
564
|
+
media,
|
|
565
|
+
bytes: buffer.length,
|
|
566
|
+
mime: diagnosticMime(mime),
|
|
567
|
+
...context,
|
|
568
|
+
durationMs: Date.now() - startedAt
|
|
569
|
+
});
|
|
570
|
+
}
|
|
438
571
|
return relayed;
|
|
439
572
|
}
|
|
440
|
-
async function relayForwardContent(content) {
|
|
573
|
+
async function relayForwardContent(content, traceId, session, node) {
|
|
441
574
|
if (!relayConfig.Enabled) {
|
|
442
575
|
return content;
|
|
443
576
|
}
|
|
@@ -447,9 +580,24 @@ async function relayForwardContent(content) {
|
|
|
447
580
|
newContent.push(element);
|
|
448
581
|
continue;
|
|
449
582
|
}
|
|
583
|
+
const startedAt = Date.now();
|
|
450
584
|
try {
|
|
451
|
-
newContent.push(await downloadAndRelay(element));
|
|
585
|
+
newContent.push(await downloadAndRelay(element, traceId, session, node));
|
|
452
586
|
} catch (error) {
|
|
587
|
+
if (traceId) {
|
|
588
|
+
writeDiagnostic({
|
|
589
|
+
traceId,
|
|
590
|
+
phase: "media-relay-result",
|
|
591
|
+
status: "error",
|
|
592
|
+
error: sanitizeError(error),
|
|
593
|
+
durationMs: Date.now() - startedAt,
|
|
594
|
+
source: session && {
|
|
595
|
+
platform: session.platform,
|
|
596
|
+
node: session.channelId
|
|
597
|
+
},
|
|
598
|
+
target: node && { platform: node.Platform, node: node.Guild }
|
|
599
|
+
});
|
|
600
|
+
}
|
|
453
601
|
throw new MediaRelayError(
|
|
454
602
|
error instanceof Error ? error.message : "relay failed",
|
|
455
603
|
networkMessageFromError(error)
|
|
@@ -460,29 +608,63 @@ async function relayForwardContent(content) {
|
|
|
460
608
|
}
|
|
461
609
|
|
|
462
610
|
// src/message.ts
|
|
463
|
-
async function MessageSendWithDecorator(ctx2, node, session, Deco) {
|
|
611
|
+
async function MessageSendWithDecorator(ctx2, node, session, Deco, traceId, attempt) {
|
|
612
|
+
const startedAt = Date.now();
|
|
464
613
|
const uuid = session.channelId + ":" + session.messageId;
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
614
|
+
writeDiagnostic({
|
|
615
|
+
traceId,
|
|
616
|
+
phase: "send-attempt",
|
|
617
|
+
attempt,
|
|
618
|
+
source: { platform: session.platform, node: session.channelId },
|
|
619
|
+
target: { platform: node.Platform, node: node.Guild },
|
|
620
|
+
elementTypes: session.elements.map((element) => element.type)
|
|
621
|
+
});
|
|
622
|
+
try {
|
|
623
|
+
const content = await Deco(session, node, traceId);
|
|
624
|
+
const res = await ctx2.bots[`${node.Platform}:${node.BotID}`].sendMessage(
|
|
625
|
+
node.Guild,
|
|
626
|
+
content
|
|
627
|
+
);
|
|
628
|
+
{
|
|
629
|
+
const mc = {
|
|
630
|
+
platform: node.Platform,
|
|
631
|
+
bot: node.BotID,
|
|
632
|
+
guild: node.Guild,
|
|
633
|
+
msgid: res[0],
|
|
634
|
+
uuid
|
|
635
|
+
};
|
|
636
|
+
if (!mc.msgid) {
|
|
637
|
+
throw new Error(`Empty Message ID`);
|
|
638
|
+
} else {
|
|
639
|
+
msgCache(mc);
|
|
640
|
+
logger.debug(`[MessageForward] to ${mc.platform} ${mc.uuid}`);
|
|
641
|
+
}
|
|
479
642
|
}
|
|
480
|
-
|
|
643
|
+
writeDiagnostic({
|
|
644
|
+
traceId,
|
|
645
|
+
phase: "send-result",
|
|
646
|
+
attempt,
|
|
647
|
+
status: "success",
|
|
648
|
+
durationMs: Date.now() - startedAt,
|
|
649
|
+
source: { platform: session.platform, node: session.channelId },
|
|
650
|
+
target: { platform: node.Platform, node: node.Guild }
|
|
651
|
+
});
|
|
652
|
+
} catch (error) {
|
|
653
|
+
writeDiagnostic({
|
|
654
|
+
traceId,
|
|
655
|
+
phase: "send-result",
|
|
656
|
+
attempt,
|
|
657
|
+
status: "error",
|
|
658
|
+
durationMs: Date.now() - startedAt,
|
|
659
|
+
source: { platform: session.platform, node: session.channelId },
|
|
660
|
+
target: { platform: node.Platform, node: node.Guild },
|
|
661
|
+
error: sanitizeError(error)
|
|
662
|
+
});
|
|
481
663
|
logger.error(
|
|
482
664
|
`ERROR:<MessageSendWithDecorator ${node.Platform}> ctx=${ctx2} ${error}`
|
|
483
665
|
);
|
|
484
666
|
throw error;
|
|
485
|
-
}
|
|
667
|
+
}
|
|
486
668
|
}
|
|
487
669
|
function sessionTypeArray(session) {
|
|
488
670
|
let contentTypes = "|";
|
|
@@ -492,9 +674,34 @@ function sessionTypeArray(session) {
|
|
|
492
674
|
return contentTypes;
|
|
493
675
|
}
|
|
494
676
|
async function MessageForward(ctx2, node, session, timeoutSec, relayEnabled) {
|
|
677
|
+
const traceId = createTraceId();
|
|
678
|
+
const source = { platform: session.platform, node: session.channelId };
|
|
679
|
+
const target = { platform: node.Platform, node: node.Guild };
|
|
495
680
|
if (!botExistsCheck(ctx2, node)) {
|
|
681
|
+
writeDiagnostic({
|
|
682
|
+
traceId,
|
|
683
|
+
phase: "forward-result",
|
|
684
|
+
status: "bot-missing",
|
|
685
|
+
source,
|
|
686
|
+
target
|
|
687
|
+
});
|
|
496
688
|
return;
|
|
497
689
|
}
|
|
690
|
+
writeDiagnostic({
|
|
691
|
+
traceId,
|
|
692
|
+
phase: "forward-start",
|
|
693
|
+
source,
|
|
694
|
+
target,
|
|
695
|
+
elementTypes: session.elements.map((element) => element.type),
|
|
696
|
+
media: session.elements.filter(
|
|
697
|
+
(element) => ["img", "image", "audio", "video", "file"].includes(element.type)
|
|
698
|
+
).map((element) => ({
|
|
699
|
+
type: element.type,
|
|
700
|
+
filename: diagnosticFilename(element.attrs?.filename),
|
|
701
|
+
mime: diagnosticMime(element.attrs?.mime),
|
|
702
|
+
size: typeof element.attrs?.size === "number" ? element.attrs.size : void 0
|
|
703
|
+
}))
|
|
704
|
+
});
|
|
498
705
|
const timeoutMs = (timeoutSec ?? 30) * 1e3;
|
|
499
706
|
const deco = relayEnabled !== false ? MsgDecorator : MsgDecoratorNoRelay;
|
|
500
707
|
function warnIfSlow(promise) {
|
|
@@ -506,8 +713,16 @@ async function MessageForward(ctx2, node, session, timeoutSec, relayEnabled) {
|
|
|
506
713
|
return promise.finally(() => clearTimeout(timer));
|
|
507
714
|
}
|
|
508
715
|
function sendDegraded(reason) {
|
|
716
|
+
writeDiagnostic({ traceId, phase: "forward-degraded", source, target });
|
|
509
717
|
const fallbackDeco = reason ? (fallbackSession, fallbackNode) => MsgDecoratorFallbackReason(fallbackSession, fallbackNode, reason) : MsgDecoratorFallback;
|
|
510
|
-
MessageSendWithDecorator(
|
|
718
|
+
MessageSendWithDecorator(
|
|
719
|
+
ctx2,
|
|
720
|
+
node,
|
|
721
|
+
session,
|
|
722
|
+
fallbackDeco,
|
|
723
|
+
traceId,
|
|
724
|
+
"fallback"
|
|
725
|
+
).catch((error) => {
|
|
511
726
|
logger.error(
|
|
512
727
|
`ERROR:<MessageSendFallback ${node.Platform}> ctx=${ctx2} ${sessionTypeArray(session)} ${error}`
|
|
513
728
|
);
|
|
@@ -519,28 +734,43 @@ async function MessageForward(ctx2, node, session, timeoutSec, relayEnabled) {
|
|
|
519
734
|
}
|
|
520
735
|
return void 0;
|
|
521
736
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
737
|
+
const traceableDeco = (sourceSession, targetNode, id) => deco(sourceSession, targetNode, id);
|
|
738
|
+
warnIfSlow(
|
|
739
|
+
MessageSendWithDecorator(
|
|
740
|
+
ctx2,
|
|
741
|
+
node,
|
|
742
|
+
session,
|
|
743
|
+
traceableDeco,
|
|
744
|
+
traceId,
|
|
745
|
+
relayEnabled === false ? "direct" : "relay"
|
|
746
|
+
)
|
|
747
|
+
).catch((firstError) => {
|
|
748
|
+
logger.error(
|
|
749
|
+
`ERROR:<MessageSend ${node.Platform}> ctx=${ctx2} ${sessionTypeArray(session)} ${firstError}`
|
|
750
|
+
);
|
|
751
|
+
if (relayEnabled !== false && firstError instanceof MediaRelayError) {
|
|
752
|
+
logger.info(
|
|
753
|
+
`[MessageForward] relay failed, retrying without relay: ${node.Platform}`
|
|
526
754
|
);
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
755
|
+
warnIfSlow(
|
|
756
|
+
MessageSendWithDecorator(
|
|
757
|
+
ctx2,
|
|
758
|
+
node,
|
|
759
|
+
session,
|
|
760
|
+
MsgDecoratorNoRelay,
|
|
761
|
+
traceId,
|
|
762
|
+
"direct-retry"
|
|
763
|
+
)
|
|
764
|
+
).catch((secondError) => {
|
|
765
|
+
logger.error(
|
|
766
|
+
`ERROR:<MessageSendDirect ${node.Platform}> ctx=${ctx2} ${sessionTypeArray(session)} ${secondError}`
|
|
530
767
|
);
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
`ERROR:<MessageSendDirect ${node.Platform}> ctx=${ctx2} ${sessionTypeArray(session)} ${secondError}`
|
|
536
|
-
);
|
|
537
|
-
sendDegraded(reasonFromError(secondError));
|
|
538
|
-
});
|
|
539
|
-
} else {
|
|
540
|
-
sendDegraded(reasonFromError(firstError));
|
|
541
|
-
}
|
|
768
|
+
sendDegraded(reasonFromError(secondError));
|
|
769
|
+
});
|
|
770
|
+
} else {
|
|
771
|
+
sendDegraded(reasonFromError(firstError));
|
|
542
772
|
}
|
|
543
|
-
);
|
|
773
|
+
});
|
|
544
774
|
}
|
|
545
775
|
async function MessageDelete(ctx2, msg) {
|
|
546
776
|
if (!botExistsCheck(ctx2, { Platform: msg.platform, BotID: msg.bot, Guild: msg.guild })) {
|
|
@@ -617,7 +847,7 @@ async function MsgMiddlewareCache(session) {
|
|
|
617
847
|
msgMiddleCacheAppend({ UUID: MsgUUIDFromSession(session), msg: elems });
|
|
618
848
|
logger.debug(`[msgMiddleCache] CACHED`);
|
|
619
849
|
}
|
|
620
|
-
async function MsgDecorator(session, node) {
|
|
850
|
+
async function MsgDecorator(session, node, traceId) {
|
|
621
851
|
let elems;
|
|
622
852
|
const _platform_out = decorators_exports[node.Platform];
|
|
623
853
|
const elemCache = msgMiddleCacheFind(MsgUUIDFromSession(session));
|
|
@@ -633,7 +863,7 @@ async function MsgDecorator(session, node) {
|
|
|
633
863
|
}
|
|
634
864
|
elems = {
|
|
635
865
|
head: elems.head,
|
|
636
|
-
content: await relayForwardContent(elems.content)
|
|
866
|
+
content: await relayForwardContent(elems.content, traceId, session, node)
|
|
637
867
|
};
|
|
638
868
|
if (_platform_out && typeof _platform_out.Decorator === "function") {
|
|
639
869
|
return _platform_out.Decorator(elems);
|
|
@@ -689,7 +919,7 @@ async function MsgDecoratorFallbackReason(session, node, reason) {
|
|
|
689
919
|
return defaultDecoratorFallback(elems, reason);
|
|
690
920
|
}
|
|
691
921
|
async function atTranslator(session, _, { head, content }) {
|
|
692
|
-
const newMsg = await new Promise((
|
|
922
|
+
const newMsg = await new Promise((resolve2) => {
|
|
693
923
|
const newContent = [];
|
|
694
924
|
for (const key in content) {
|
|
695
925
|
const element = content[key];
|
|
@@ -703,7 +933,7 @@ async function atTranslator(session, _, { head, content }) {
|
|
|
703
933
|
newContent.push(element);
|
|
704
934
|
}
|
|
705
935
|
}
|
|
706
|
-
|
|
936
|
+
resolve2({ head, content: newContent });
|
|
707
937
|
});
|
|
708
938
|
return newMsg;
|
|
709
939
|
}
|
|
@@ -725,6 +955,21 @@ async function quoteTranslator(session, node, { head, content }) {
|
|
|
725
955
|
return { head, content };
|
|
726
956
|
}
|
|
727
957
|
|
|
958
|
+
// src/console.ts
|
|
959
|
+
var import_path2 = require("path");
|
|
960
|
+
function consoleInit(ctx2) {
|
|
961
|
+
ctx2.inject(["console"], (ctx3) => {
|
|
962
|
+
const console = ctx3.console;
|
|
963
|
+
console.addListener("forward-hime:get-diagnostics", readDiagnosticLogs, {
|
|
964
|
+
authority: 4
|
|
965
|
+
});
|
|
966
|
+
console.addEntry({
|
|
967
|
+
dev: (0, import_path2.resolve)(__dirname, "../client/index.ts"),
|
|
968
|
+
prod: (0, import_path2.resolve)(__dirname, "client")
|
|
969
|
+
});
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
|
|
728
973
|
// src/index.ts
|
|
729
974
|
var name = `forward hime - \u8F6C\u53D1\u59EC`;
|
|
730
975
|
var usage = `
|
|
@@ -740,11 +985,13 @@ var usage = `
|
|
|
740
985
|
---`;
|
|
741
986
|
var reusable = true;
|
|
742
987
|
var inject = {
|
|
743
|
-
optional: ["cache"]
|
|
988
|
+
optional: ["cache", "console"]
|
|
744
989
|
};
|
|
745
990
|
var Config = createConfig();
|
|
746
991
|
function apply(ctx2, cfg) {
|
|
747
992
|
loggerInit(ctx2);
|
|
993
|
+
diagnosticsInit(ctx2);
|
|
994
|
+
consoleInit(ctx2);
|
|
748
995
|
msgCacheInit(ctx2, cfg);
|
|
749
996
|
decoratorInit(cfg);
|
|
750
997
|
ctx2.on("message-created", async (session) => {
|
|
@@ -814,7 +1061,7 @@ function apply(ctx2, cfg) {
|
|
|
814
1061
|
});
|
|
815
1062
|
});
|
|
816
1063
|
ctx2.on("message-updated", async (session) => {
|
|
817
|
-
logger.debug("[message-updated]", session.messageId
|
|
1064
|
+
logger.debug("[message-updated]", session.messageId);
|
|
818
1065
|
});
|
|
819
1066
|
}
|
|
820
1067
|
// Annotate the CommonJS export names for ESM import in node:
|
package/lib/relay.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Element } from "koishi";
|
|
2
2
|
import { ConfigSet } from "./config";
|
|
3
|
+
import { Session } from "koishi";
|
|
4
|
+
import { ForwardNode } from "./config";
|
|
3
5
|
export declare class MediaRelayError extends Error {
|
|
4
6
|
userMessage: string;
|
|
5
7
|
constructor(message: string, userMessage: string);
|
|
6
8
|
}
|
|
7
9
|
export declare function relayInit(cfg: ConfigSet): void;
|
|
8
|
-
export declare function relayForwardContent(content: Element[]): Promise<Element[]>;
|
|
10
|
+
export declare function relayForwardContent(content: Element[], traceId?: string, session?: Session, node?: ForwardNode): Promise<Element[]>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koishi-plugin-forward-hime",
|
|
3
3
|
"description": "多群组消息互通",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.2-alpha.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "xianii",
|
|
7
7
|
"email": "jiyucheng007@gmail.com"
|
|
@@ -57,7 +57,8 @@
|
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"prepare": "husky",
|
|
60
|
-
"build": "rm -f tsconfig.tsbuildinfo && rm -rf lib && tsc -b && esbuild src/index.ts --bundle --platform=node --packages=external --outfile=lib/index.js",
|
|
60
|
+
"build": "rm -f tsconfig.tsbuildinfo && rm -rf lib && tsc -b && esbuild src/index.ts --bundle --platform=node --packages=external --outfile=lib/index.js && esbuild client/index.ts --bundle --platform=browser --format=esm --packages=external --outfile=lib/client/index.js",
|
|
61
|
+
"test": "node --test",
|
|
61
62
|
"eslint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
62
63
|
"format": "prettier --write .",
|
|
63
64
|
"pub": "npm publish --access public"
|