koishi-plugin-forward-hime 1.4.2-alpha.0 → 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 +289 -45
- 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";
|
|
@@ -364,9 +457,23 @@ function networkMessageFromError(error) {
|
|
|
364
457
|
}
|
|
365
458
|
return "\u7F51\u7EDC\u95EE\u9898\uFF1A\u5A92\u4F53\u6587\u4EF6\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u3002";
|
|
366
459
|
}
|
|
367
|
-
async function downloadAndRelay(element) {
|
|
460
|
+
async function downloadAndRelay(element, traceId, session, node) {
|
|
461
|
+
const startedAt = Date.now();
|
|
368
462
|
const kind = normalizeMediaType(element.type);
|
|
369
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
|
+
}
|
|
370
477
|
if (!src) {
|
|
371
478
|
throw new MediaRelayError(
|
|
372
479
|
`missing media src for ${element.type}`,
|
|
@@ -377,6 +484,17 @@ async function downloadAndRelay(element) {
|
|
|
377
484
|
const cacheKey = getCacheKey(element);
|
|
378
485
|
const cacheItem = relayCache.get(cacheKey);
|
|
379
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
|
+
}
|
|
380
498
|
return relayElementFromCache(cacheItem);
|
|
381
499
|
}
|
|
382
500
|
const timeoutSignal = typeof AbortSignal.timeout === "function" ? AbortSignal.timeout(relayConfig.RequestTimeoutSec * 1e3) : void 0;
|
|
@@ -438,9 +556,21 @@ async function downloadAndRelay(element) {
|
|
|
438
556
|
buffer,
|
|
439
557
|
filename
|
|
440
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
|
+
}
|
|
441
571
|
return relayed;
|
|
442
572
|
}
|
|
443
|
-
async function relayForwardContent(content) {
|
|
573
|
+
async function relayForwardContent(content, traceId, session, node) {
|
|
444
574
|
if (!relayConfig.Enabled) {
|
|
445
575
|
return content;
|
|
446
576
|
}
|
|
@@ -450,9 +580,24 @@ async function relayForwardContent(content) {
|
|
|
450
580
|
newContent.push(element);
|
|
451
581
|
continue;
|
|
452
582
|
}
|
|
583
|
+
const startedAt = Date.now();
|
|
453
584
|
try {
|
|
454
|
-
newContent.push(await downloadAndRelay(element));
|
|
585
|
+
newContent.push(await downloadAndRelay(element, traceId, session, node));
|
|
455
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
|
+
}
|
|
456
601
|
throw new MediaRelayError(
|
|
457
602
|
error instanceof Error ? error.message : "relay failed",
|
|
458
603
|
networkMessageFromError(error)
|
|
@@ -463,29 +608,63 @@ async function relayForwardContent(content) {
|
|
|
463
608
|
}
|
|
464
609
|
|
|
465
610
|
// src/message.ts
|
|
466
|
-
async function MessageSendWithDecorator(ctx2, node, session, Deco) {
|
|
611
|
+
async function MessageSendWithDecorator(ctx2, node, session, Deco, traceId, attempt) {
|
|
612
|
+
const startedAt = Date.now();
|
|
467
613
|
const uuid = session.channelId + ":" + session.messageId;
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
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
|
+
}
|
|
482
642
|
}
|
|
483
|
-
|
|
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
|
+
});
|
|
484
663
|
logger.error(
|
|
485
664
|
`ERROR:<MessageSendWithDecorator ${node.Platform}> ctx=${ctx2} ${error}`
|
|
486
665
|
);
|
|
487
666
|
throw error;
|
|
488
|
-
}
|
|
667
|
+
}
|
|
489
668
|
}
|
|
490
669
|
function sessionTypeArray(session) {
|
|
491
670
|
let contentTypes = "|";
|
|
@@ -495,9 +674,34 @@ function sessionTypeArray(session) {
|
|
|
495
674
|
return contentTypes;
|
|
496
675
|
}
|
|
497
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 };
|
|
498
680
|
if (!botExistsCheck(ctx2, node)) {
|
|
681
|
+
writeDiagnostic({
|
|
682
|
+
traceId,
|
|
683
|
+
phase: "forward-result",
|
|
684
|
+
status: "bot-missing",
|
|
685
|
+
source,
|
|
686
|
+
target
|
|
687
|
+
});
|
|
499
688
|
return;
|
|
500
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
|
+
});
|
|
501
705
|
const timeoutMs = (timeoutSec ?? 30) * 1e3;
|
|
502
706
|
const deco = relayEnabled !== false ? MsgDecorator : MsgDecoratorNoRelay;
|
|
503
707
|
function warnIfSlow(promise) {
|
|
@@ -509,8 +713,16 @@ async function MessageForward(ctx2, node, session, timeoutSec, relayEnabled) {
|
|
|
509
713
|
return promise.finally(() => clearTimeout(timer));
|
|
510
714
|
}
|
|
511
715
|
function sendDegraded(reason) {
|
|
716
|
+
writeDiagnostic({ traceId, phase: "forward-degraded", source, target });
|
|
512
717
|
const fallbackDeco = reason ? (fallbackSession, fallbackNode) => MsgDecoratorFallbackReason(fallbackSession, fallbackNode, reason) : MsgDecoratorFallback;
|
|
513
|
-
MessageSendWithDecorator(
|
|
718
|
+
MessageSendWithDecorator(
|
|
719
|
+
ctx2,
|
|
720
|
+
node,
|
|
721
|
+
session,
|
|
722
|
+
fallbackDeco,
|
|
723
|
+
traceId,
|
|
724
|
+
"fallback"
|
|
725
|
+
).catch((error) => {
|
|
514
726
|
logger.error(
|
|
515
727
|
`ERROR:<MessageSendFallback ${node.Platform}> ctx=${ctx2} ${sessionTypeArray(session)} ${error}`
|
|
516
728
|
);
|
|
@@ -522,28 +734,43 @@ async function MessageForward(ctx2, node, session, timeoutSec, relayEnabled) {
|
|
|
522
734
|
}
|
|
523
735
|
return void 0;
|
|
524
736
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
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}`
|
|
529
754
|
);
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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}`
|
|
533
767
|
);
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
`ERROR:<MessageSendDirect ${node.Platform}> ctx=${ctx2} ${sessionTypeArray(session)} ${secondError}`
|
|
539
|
-
);
|
|
540
|
-
sendDegraded(reasonFromError(secondError));
|
|
541
|
-
});
|
|
542
|
-
} else {
|
|
543
|
-
sendDegraded(reasonFromError(firstError));
|
|
544
|
-
}
|
|
768
|
+
sendDegraded(reasonFromError(secondError));
|
|
769
|
+
});
|
|
770
|
+
} else {
|
|
771
|
+
sendDegraded(reasonFromError(firstError));
|
|
545
772
|
}
|
|
546
|
-
);
|
|
773
|
+
});
|
|
547
774
|
}
|
|
548
775
|
async function MessageDelete(ctx2, msg) {
|
|
549
776
|
if (!botExistsCheck(ctx2, { Platform: msg.platform, BotID: msg.bot, Guild: msg.guild })) {
|
|
@@ -620,7 +847,7 @@ async function MsgMiddlewareCache(session) {
|
|
|
620
847
|
msgMiddleCacheAppend({ UUID: MsgUUIDFromSession(session), msg: elems });
|
|
621
848
|
logger.debug(`[msgMiddleCache] CACHED`);
|
|
622
849
|
}
|
|
623
|
-
async function MsgDecorator(session, node) {
|
|
850
|
+
async function MsgDecorator(session, node, traceId) {
|
|
624
851
|
let elems;
|
|
625
852
|
const _platform_out = decorators_exports[node.Platform];
|
|
626
853
|
const elemCache = msgMiddleCacheFind(MsgUUIDFromSession(session));
|
|
@@ -636,7 +863,7 @@ async function MsgDecorator(session, node) {
|
|
|
636
863
|
}
|
|
637
864
|
elems = {
|
|
638
865
|
head: elems.head,
|
|
639
|
-
content: await relayForwardContent(elems.content)
|
|
866
|
+
content: await relayForwardContent(elems.content, traceId, session, node)
|
|
640
867
|
};
|
|
641
868
|
if (_platform_out && typeof _platform_out.Decorator === "function") {
|
|
642
869
|
return _platform_out.Decorator(elems);
|
|
@@ -692,7 +919,7 @@ async function MsgDecoratorFallbackReason(session, node, reason) {
|
|
|
692
919
|
return defaultDecoratorFallback(elems, reason);
|
|
693
920
|
}
|
|
694
921
|
async function atTranslator(session, _, { head, content }) {
|
|
695
|
-
const newMsg = await new Promise((
|
|
922
|
+
const newMsg = await new Promise((resolve2) => {
|
|
696
923
|
const newContent = [];
|
|
697
924
|
for (const key in content) {
|
|
698
925
|
const element = content[key];
|
|
@@ -706,7 +933,7 @@ async function atTranslator(session, _, { head, content }) {
|
|
|
706
933
|
newContent.push(element);
|
|
707
934
|
}
|
|
708
935
|
}
|
|
709
|
-
|
|
936
|
+
resolve2({ head, content: newContent });
|
|
710
937
|
});
|
|
711
938
|
return newMsg;
|
|
712
939
|
}
|
|
@@ -728,6 +955,21 @@ async function quoteTranslator(session, node, { head, content }) {
|
|
|
728
955
|
return { head, content };
|
|
729
956
|
}
|
|
730
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
|
+
|
|
731
973
|
// src/index.ts
|
|
732
974
|
var name = `forward hime - \u8F6C\u53D1\u59EC`;
|
|
733
975
|
var usage = `
|
|
@@ -743,11 +985,13 @@ var usage = `
|
|
|
743
985
|
---`;
|
|
744
986
|
var reusable = true;
|
|
745
987
|
var inject = {
|
|
746
|
-
optional: ["cache"]
|
|
988
|
+
optional: ["cache", "console"]
|
|
747
989
|
};
|
|
748
990
|
var Config = createConfig();
|
|
749
991
|
function apply(ctx2, cfg) {
|
|
750
992
|
loggerInit(ctx2);
|
|
993
|
+
diagnosticsInit(ctx2);
|
|
994
|
+
consoleInit(ctx2);
|
|
751
995
|
msgCacheInit(ctx2, cfg);
|
|
752
996
|
decoratorInit(cfg);
|
|
753
997
|
ctx2.on("message-created", async (session) => {
|
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.2-alpha.
|
|
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"
|