mioku 1.0.0 → 1.0.2
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/dist/cli/index.cjs +77 -46
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +77 -46
- package/dist/cli/index.js.map +1 -1
- package/dist/{exec-gpip41w9.cjs → exec-B4lXl4No.cjs} +7 -19
- package/dist/exec-B4lXl4No.cjs.map +1 -0
- package/dist/{exec-CUvS9y6X.js → exec-DmpZrf3S.js} +7 -19
- package/dist/exec-DmpZrf3S.js.map +1 -0
- package/dist/index.cjs +146 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +78 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +146 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/exec-CUvS9y6X.js.map +0 -1
- package/dist/exec-gpip41w9.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildSpawnPlan, commandExists, resolveCommand, runCommand, runCommandInherit, version } from "./exec-
|
|
1
|
+
import { buildSpawnPlan, commandExists, resolveCommand, runCommand, runCommandInherit, version } from "./exec-DmpZrf3S.js";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import * as fs$5 from "node:fs";
|
|
4
4
|
import * as fs$4 from "node:fs";
|
|
@@ -517,6 +517,12 @@ const segment = {
|
|
|
517
517
|
json(data) {
|
|
518
518
|
return new MessageSegmentImpl("json", { data: typeof data === "string" ? data : JSON.stringify(data) });
|
|
519
519
|
},
|
|
520
|
+
markdown(content) {
|
|
521
|
+
return new MessageSegmentImpl("markdown", { content });
|
|
522
|
+
},
|
|
523
|
+
button(options) {
|
|
524
|
+
return new MessageSegmentImpl("button", { ...options });
|
|
525
|
+
},
|
|
520
526
|
raw(type, data, attachment) {
|
|
521
527
|
return new MessageSegmentImpl(type, data, attachment);
|
|
522
528
|
}
|
|
@@ -862,6 +868,7 @@ var AdapterContextImpl = class {
|
|
|
862
868
|
#logger;
|
|
863
869
|
#emit;
|
|
864
870
|
#pendingStarts = new Set();
|
|
871
|
+
#crossDedup;
|
|
865
872
|
constructor(options) {
|
|
866
873
|
this.#state = options.state;
|
|
867
874
|
this.#bots = options.bots;
|
|
@@ -870,6 +877,7 @@ var AdapterContextImpl = class {
|
|
|
870
877
|
this.#capabilities = options.capabilities;
|
|
871
878
|
this.#logger = options.logger;
|
|
872
879
|
this.#emit = options.emit;
|
|
880
|
+
this.#crossDedup = options.crossDedup ?? null;
|
|
873
881
|
}
|
|
874
882
|
registerBot(bot) {
|
|
875
883
|
return this.#bots.register(bot);
|
|
@@ -933,6 +941,20 @@ var AdapterContextImpl = class {
|
|
|
933
941
|
};
|
|
934
942
|
}
|
|
935
943
|
async dispatch(event) {
|
|
944
|
+
if (event.kind === "message") {
|
|
945
|
+
const senderId = event.user_id;
|
|
946
|
+
if (senderId != null) {
|
|
947
|
+
const senderBot = this.#bots.pick(senderId);
|
|
948
|
+
if (senderBot && senderBot.bot_id !== event.self_id) {
|
|
949
|
+
this.#logger.debug(`拦截来自其他已连接 bot 的消息: sender=${senderId} self=${event.self_id}`);
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (this.#crossDedup?.isDuplicate(event)) {
|
|
954
|
+
this.#logger.debug(`丢弃跨适配器重复消息: adapter=${event.identity?.adapter ?? ""} sender=${event.user_id ?? ""}`);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
}
|
|
936
958
|
await this.#bus.dispatch(event);
|
|
937
959
|
}
|
|
938
960
|
listen(route, handler) {
|
|
@@ -2414,6 +2436,9 @@ const toInstance = (bot, status) => {
|
|
|
2414
2436
|
connected_at: bot.connected_at,
|
|
2415
2437
|
impl: status?.impl,
|
|
2416
2438
|
version: status?.version,
|
|
2439
|
+
protocol: status?.protocol,
|
|
2440
|
+
platform: status?.platform,
|
|
2441
|
+
platformVersion: status?.platform_version,
|
|
2417
2442
|
stats: normalizeStats(status?.stats, data),
|
|
2418
2443
|
data
|
|
2419
2444
|
};
|
|
@@ -2440,6 +2465,7 @@ const buildAdapterReport = async (options) => {
|
|
|
2440
2465
|
return {
|
|
2441
2466
|
name,
|
|
2442
2467
|
version: known?.version,
|
|
2468
|
+
impl: known?.impl,
|
|
2443
2469
|
loaded: known != null,
|
|
2444
2470
|
instances: paired.filter((entry) => entry.adapter === name).map((entry) => entry.instance)
|
|
2445
2471
|
};
|
|
@@ -2464,8 +2490,11 @@ const formatStats = (stats) => {
|
|
|
2464
2490
|
return segments.join(" · ");
|
|
2465
2491
|
};
|
|
2466
2492
|
const formatInstance = (instance) => {
|
|
2467
|
-
const
|
|
2468
|
-
|
|
2493
|
+
const tags = [
|
|
2494
|
+
[instance.impl, instance.version].filter(Boolean).join("/"),
|
|
2495
|
+
instance.platform ? `${instance.platform}${instance.platformVersion ? ` v${instance.platformVersion}` : ""}` : "",
|
|
2496
|
+
instance.online ? "" : "🔴 离线"
|
|
2497
|
+
].filter(Boolean);
|
|
2469
2498
|
const lines = [`👤 ${instance.nickname || "(未命名)"} (${instance.bot_id})${tags.length ? ` · ${tags.join(" · ")}` : ""}`];
|
|
2470
2499
|
const uptime = instance.online && instance.connected_at != null ? `⏳ 已连接 ${prettyMs(Date.now() - instance.connected_at, {
|
|
2471
2500
|
hideYear: true,
|
|
@@ -2482,9 +2511,16 @@ const summarize = (entry) => {
|
|
|
2482
2511
|
if (online === total) return `🟢 ${localNum(total)} 实例`;
|
|
2483
2512
|
return `${online === 0 ? "🔴" : "🟡"} ${localNum(online)}/${localNum(total)} 实例在线`;
|
|
2484
2513
|
};
|
|
2514
|
+
/** `名称@包版本[/实现库 v版本]` */
|
|
2515
|
+
const formatAdapterName = (entry) => {
|
|
2516
|
+
const base = `${entry.name}${entry.version ? `@${entry.version}` : ""}`;
|
|
2517
|
+
if (!entry.impl) return base;
|
|
2518
|
+
const impl = `${entry.impl.name}${entry.impl.version ? ` v${entry.impl.version}` : ""}`;
|
|
2519
|
+
return `${base}/${impl}`;
|
|
2520
|
+
};
|
|
2485
2521
|
const formatAdapterReport = (report) => {
|
|
2486
2522
|
if (!report.adapters.length) return "〓 🔌 mioku 适配器 〓\n(未加载任何适配器)";
|
|
2487
|
-
const lines = report.adapters.flatMap((entry) => [`🔌 ${entry
|
|
2523
|
+
const lines = report.adapters.flatMap((entry) => [`🔌 ${formatAdapterName(entry)} · ${summarize(entry)}`, ...entry.instances.flatMap(formatInstance)]);
|
|
2488
2524
|
return ["〓 🔌 mioku 适配器 〓", ...lines].join("\n");
|
|
2489
2525
|
};
|
|
2490
2526
|
|
|
@@ -4027,19 +4063,21 @@ const core = definePlugin({
|
|
|
4027
4063
|
const collectBots = () => ctx.bots;
|
|
4028
4064
|
/** 已加载的适配器优先,再补上只在 bot 上出现过的适配器名 */
|
|
4029
4065
|
const collectAdapters = () => {
|
|
4030
|
-
const
|
|
4031
|
-
name
|
|
4032
|
-
version: adapter
|
|
4033
|
-
|
|
4066
|
+
const describe = (name, adapter) => ({
|
|
4067
|
+
name,
|
|
4068
|
+
version: adapter?.version,
|
|
4069
|
+
impl: adapter?.impl ? {
|
|
4070
|
+
name: adapter.impl.name,
|
|
4071
|
+
version: adapter.impl.version
|
|
4072
|
+
} : void 0
|
|
4073
|
+
});
|
|
4074
|
+
const list = ctx.adapters.map((adapter) => describe(adapter.name, adapter));
|
|
4034
4075
|
const seen = new Set(list.map((adapter) => adapter.name));
|
|
4035
4076
|
for (const bot of ctx.bots) {
|
|
4036
4077
|
const name = String(bot.adapter);
|
|
4037
4078
|
if (seen.has(name)) continue;
|
|
4038
4079
|
seen.add(name);
|
|
4039
|
-
list.push(
|
|
4040
|
-
name,
|
|
4041
|
-
version: ctx.getAdapter(name)?.version
|
|
4042
|
-
});
|
|
4080
|
+
list.push(describe(name, ctx.getAdapter(name)));
|
|
4043
4081
|
}
|
|
4044
4082
|
return list;
|
|
4045
4083
|
};
|
|
@@ -4693,6 +4731,96 @@ const loadLocalPlugin = async (jiti, name, resolvedPath) => {
|
|
|
4693
4731
|
return await loadFromEntry(jiti, entry, name);
|
|
4694
4732
|
};
|
|
4695
4733
|
|
|
4734
|
+
//#endregion
|
|
4735
|
+
//#region src/runtime/cross-adapter-dedup.ts
|
|
4736
|
+
const BUCKET_SLACK = 1;
|
|
4737
|
+
const PRUNE_INTERVAL = 128;
|
|
4738
|
+
const TTL_MS = 15e3;
|
|
4739
|
+
const MAX_SIZE = 4096;
|
|
4740
|
+
const MAX_SEGMENTS = 16;
|
|
4741
|
+
const MAX_TEXT = 256;
|
|
4742
|
+
/**
|
|
4743
|
+
* 把消息段序列化为跨适配器稳定的内容指纹:
|
|
4744
|
+
* 文本/@/表情取内容,媒体段只取类型(url/文件名跨协议不稳定)
|
|
4745
|
+
*/
|
|
4746
|
+
const contentFingerprintOf = (event) => {
|
|
4747
|
+
const parts = [];
|
|
4748
|
+
for (const seg of event.message) {
|
|
4749
|
+
if (parts.length >= MAX_SEGMENTS) break;
|
|
4750
|
+
if (seg.type === "reply") continue;
|
|
4751
|
+
const data = seg.data ?? {};
|
|
4752
|
+
if (seg.type === "text") {
|
|
4753
|
+
const text$1 = typeof data.text === "string" ? data.text : "";
|
|
4754
|
+
parts.push(`t:${text$1.slice(0, MAX_TEXT)}`);
|
|
4755
|
+
} else if (seg.type === "at") {
|
|
4756
|
+
const target = data.qq ?? data.target;
|
|
4757
|
+
parts.push(`a:${target == null ? "" : String(target)}`);
|
|
4758
|
+
} else if (seg.type === "face") parts.push(`f:${data.id == null ? "" : String(data.id)}`);
|
|
4759
|
+
else parts.push(seg.type);
|
|
4760
|
+
}
|
|
4761
|
+
return parts.join("|");
|
|
4762
|
+
};
|
|
4763
|
+
const scopeKeyOf = (event, content) => [
|
|
4764
|
+
event.self_id ?? "",
|
|
4765
|
+
event.message_type ?? "",
|
|
4766
|
+
event.group_id ?? event.user_id ?? "",
|
|
4767
|
+
event.user_id ?? "",
|
|
4768
|
+
content
|
|
4769
|
+
].join("|");
|
|
4770
|
+
/**
|
|
4771
|
+
* 跨适配器消息去重(L2)
|
|
4772
|
+
*/
|
|
4773
|
+
var CrossAdapterMessageDeduplicator = class {
|
|
4774
|
+
#entries = new Map();
|
|
4775
|
+
#inserts = 0;
|
|
4776
|
+
#dropped = 0;
|
|
4777
|
+
/** 本次运行以来被丢弃的跨适配器重复消息数 */
|
|
4778
|
+
get dropped() {
|
|
4779
|
+
return this.#dropped;
|
|
4780
|
+
}
|
|
4781
|
+
isDuplicate(event) {
|
|
4782
|
+
if (event.kind !== "message") return false;
|
|
4783
|
+
const messageEvent = event;
|
|
4784
|
+
const adapter = messageEvent.identity?.adapter ?? "";
|
|
4785
|
+
const now = Date.now();
|
|
4786
|
+
const bucket = Math.floor((messageEvent.time ?? now) / 1e3);
|
|
4787
|
+
const key = scopeKeyOf(messageEvent, contentFingerprintOf(messageEvent));
|
|
4788
|
+
const existing = this.#entries.get(key);
|
|
4789
|
+
if (existing) {
|
|
4790
|
+
if (Math.abs(existing.bucket - bucket) <= BUCKET_SLACK) {
|
|
4791
|
+
if (existing.adapter !== adapter) {
|
|
4792
|
+
this.#dropped++;
|
|
4793
|
+
return true;
|
|
4794
|
+
}
|
|
4795
|
+
existing.bucket = bucket;
|
|
4796
|
+
existing.at = now;
|
|
4797
|
+
return false;
|
|
4798
|
+
}
|
|
4799
|
+
existing.bucket = bucket;
|
|
4800
|
+
existing.at = now;
|
|
4801
|
+
existing.adapter = adapter;
|
|
4802
|
+
return false;
|
|
4803
|
+
}
|
|
4804
|
+
this.#entries.set(key, {
|
|
4805
|
+
adapter,
|
|
4806
|
+
bucket,
|
|
4807
|
+
at: now
|
|
4808
|
+
});
|
|
4809
|
+
this.#inserts++;
|
|
4810
|
+
if (this.#inserts >= PRUNE_INTERVAL || this.#entries.size > MAX_SIZE) {
|
|
4811
|
+
this.#inserts = 0;
|
|
4812
|
+
this.#prune(now);
|
|
4813
|
+
}
|
|
4814
|
+
return false;
|
|
4815
|
+
}
|
|
4816
|
+
#prune(now) {
|
|
4817
|
+
for (const [key, entry] of this.#entries) if (now - entry.at > TTL_MS) this.#entries.delete(key);
|
|
4818
|
+
}
|
|
4819
|
+
clear() {
|
|
4820
|
+
this.#entries.clear();
|
|
4821
|
+
}
|
|
4822
|
+
};
|
|
4823
|
+
|
|
4696
4824
|
//#endregion
|
|
4697
4825
|
//#region src/runtime/runtime.ts
|
|
4698
4826
|
const BUILTIN_PLUGINS$1 = [...BUILTIN_PLUGINS];
|
|
@@ -4713,6 +4841,7 @@ var MiokuRuntime = class {
|
|
|
4713
4841
|
#enabledPlugins = new Map();
|
|
4714
4842
|
#driverFactory;
|
|
4715
4843
|
#builtinPlugins;
|
|
4844
|
+
#crossDedup;
|
|
4716
4845
|
#started = false;
|
|
4717
4846
|
#stopped = false;
|
|
4718
4847
|
constructor(options) {
|
|
@@ -4729,6 +4858,7 @@ var MiokuRuntime = class {
|
|
|
4729
4858
|
});
|
|
4730
4859
|
this.#bots = new BotRegistry();
|
|
4731
4860
|
this.#capabilities = new CapabilityRegistry();
|
|
4861
|
+
this.#crossDedup = options.dedup?.crossAdapter === false ? null : new CrossAdapterMessageDeduplicator();
|
|
4732
4862
|
}
|
|
4733
4863
|
get cwd() {
|
|
4734
4864
|
return this.#cwd;
|
|
@@ -4849,7 +4979,8 @@ var MiokuRuntime = class {
|
|
|
4849
4979
|
driver: this.#driver,
|
|
4850
4980
|
capabilities: this.#capabilities,
|
|
4851
4981
|
logger: this.#logger.child({ adapter: state.definition.name }),
|
|
4852
|
-
emit: (event) => this.#emitLifecycle(event)
|
|
4982
|
+
emit: (event) => this.#emitLifecycle(event),
|
|
4983
|
+
crossDedup: this.#crossDedup
|
|
4853
4984
|
});
|
|
4854
4985
|
}
|
|
4855
4986
|
async #loadPlugin(plugin, type) {
|
|
@@ -5199,7 +5330,8 @@ const startRuntime = async (options = {}) => {
|
|
|
5199
5330
|
const runtime = createRuntime({
|
|
5200
5331
|
cwd,
|
|
5201
5332
|
logger,
|
|
5202
|
-
builtinPlugins: getBuiltinPlugins()
|
|
5333
|
+
builtinPlugins: getBuiltinPlugins(),
|
|
5334
|
+
dedup: { crossAdapter: botConfig.dedup?.cross_adapter ?? true }
|
|
5203
5335
|
});
|
|
5204
5336
|
const pluginDir = path$1.resolve(cwd, botConfig.plugins_dir ?? "plugins");
|
|
5205
5337
|
logger.info(colors$1.dim("=".repeat(40)));
|